About exiting value of grep - linux

(echo "123" | grep "xyz" > /dev/null) && echo $
(echo "123" | grep "123" > /dev/null) && echo $?
Command1 hasn't any output.
Command2 output '0'.
See the help of grep.
EXIT STATUS
The grep utility exits with one of the following values:
0 One or more lines were selected.
1 No lines were selected.
>1 An error occurred.
I feel confused about "expression1 && expression2".
If expression1 is true , expression2 will be executed.Is that right?
If grep matched "123", it will return zero(0 equal to false).If return zero, expression1 will be false.Why echo $? has been executed when expression1 is false?

Your presumption that 0 is equal to false is not correct. It's the opposite. 0 means that a command executed successfully, just as the help for grep says. Therefore, any non-zero exit status is considered an error, hence false. This is also the case for the test command commonly used in if statements.
With this in mind, the result of this script is as expected.

Related

What's the actual value of an if-statement executing a command?

I do not quite understand what the following if-statement actually does, in the sense of what the condition outputs could possibly be:
if linux-command-1 | linux-command-2 | linux-command-3 > /dev/null
I understand the execution as the following:
Linux Command 1 gets executed, it's output is PIPED into Linux-Command-2 as input.
Linux Command 2 gets executed with LC1's input, it's output is PIPED into Linux-Command-3.
Linux Command 3 gets executed with LC2's input, it's output is redirected into /dev/null, basically doesn't appear.
But what about the actual if-statement? What's responsible for it becoming true or false?
To further elaborate, here's an example with actual commands:
if ps ax | grep -v grep | grep terminator > /dev/null
then
echo "Success"
else
echo "Fail"
fi
I do know that the functionality behaves in the way that if any output occurs (Process is running) in that execution the condition is True, if nothing occurs (Process not running), the condition is False.
But I don't understand why or how it's coming to that conclusion? Is the shell if statement always expecting a string output as True?
I've also just discovered pgrep but the question would also remain if the statement were
if pgreg -f terminator > /dev/null
In your case you are testing the exit status of grep itself, which will return false (1) if there was no match and true (0) if there was one
if ps ax | grep -v grep | grep terminator > /dev/null
then
echo "Success"
else
echo "Fail"
fi
you could put a "-q" instead of redirection to /dev/null
if ps ax | grep -v grep | grep -q terminator
then
echo "Success"
else
echo "Fail"
fi
I would execute my commad and test the $?
ps ax | grep -v grep | grep terminator
if [ $? -eq 0 ]; then
echo 'it is ok'
else
echo 'is is ko'
fi
If you do:
if linux-command-1 | linux-command-2 | linux-command-3 > /dev/null
only the result of the last command matter
if everything is important put "&&" instead
if ps -ae | grep 'bash' | grep 'pty0' && ls . >/dev/null; then
echo "bash is in the house"
fi
that will fail because there is no not_exist
if ps -ae | grep 'bash' | grep 'pty0' && ls not_exist >/dev/null; then
echo "bash is in the house"
fi

if command A result has *not* in it, then do command B [duplicate]

In one line of bash, how do I return an exit status of 0 when the output of /usr/local/bin/monit --version doesn't contain exactly 5.5 and an exit status of 1 when it does?
! /usr/local/bin/monit --version | grep -q 5.5
(grep returns an exit-status of 0 if it finds a match, and 1 otherwise. The -q option, "quiet", tells it not to print any match it finds; in other words, it tells grep that the only thing you want is its return-value. The ! at the beginning inverts the exit-status of the whole pipeline.)
Edited to add: Alternatively, if you want to do this in "pure Bash" (rather than calling grep), you can write:
[[ $(/usr/local/bin/monit --version) != *5.5* ]]
([[...]] is explained in ยง3.2.4.2 "Conditional Constructs" of the Bash Reference Manual. *5.5* is just like in fileglobs: zero or more characters, plus 5.5, plus zero or more characters.)
[ $(/usr/local/bin/monit --version) == "5.5" ]
eg-1: check for success
[ $(/usr/local/bin/monit --version) == "5.5" ] && echo "OK"
eg-2: check for failure
[ $(/usr/local/bin/monit --version) == "5.5" ] || echo "NOT OK"
or, to just check if the output contains 5.5:
[[ $(/usr/local/bin/monit --version) =~ "5.5" ]] || echo "NOT OK"
Test the return value of grep:
sudo service xyz status | grep 'not' &> /dev/null
if [ $? == 0 ]; then
echo "whateveryouwant"
fi
I would recommend cron, it works fine with SALT stack

The return code from 'grep' is not as expected on Linux

I'm struggling to see why the following is returning a code of 1.
echo 'Total' | grep -c No
0
So "No" doesn't exists in "Total". But then looking up its return code I'm seeing it as 1.
echo $?
1
Why is return code showing up as 1? Is there a way to get around this?
According to man grep page, -c flag is for
-c, --count
Suppress normal output; instead print a count of matching lines for each input file.
So what you are seeing is the count of the match and not to be confused with the exit code of the grep match. The code 1 is because of no lines matching from the input.
Have a look at the other case,
echo 'No' | grep -c No
1
echo $?
0
Also to read on EXIT CODES on man grep page,
EXIT STATUS
Normally the exit status is 0 if a line is selected, 1 if no lines were selected, and 2 if an error occurred.
The exit code is 1 because nothing was matched by grep.
EXIT STATUS
The exit status is 0 if selected lines are found, and 1 if not found.
If an error occurred the exit status is 2. (Note: POSIX error handling
code should check for '2' or greater.)
The output is zero because the count of 'Total' is zero. This due to the -c option:
-c, --count
Suppress normal output; instead print a count of matching lines
for each input file. With the -v, --invert-match option (see
below), count non-matching lines. (-c is specified by POSIX.)
If you would like to force an exit code of 0, you can just append || true to your command:
echo 'Total' | grep -c No || true

bash empty string/command

I found some strange thing in bash and I can't understand how it works.
[test ~]$ a=""
[test ~]$ $a && echo 1
1
[test ~]$ $a
[test ~]$ echo $?
0
Why does $a (which is empty) return 0? Is it somehow transformed to empty command?
If I add quotes or write empty string before &&, it will return error. While empty command returns 0.
[test ~]$ "$a" && echo 1
-bash: : command not found
[test ~]$ "" && echo 1
-bash: : command not found
[test ~]$ `` && echo 1
1
So, what is happening when I type $a?
You seem to confuse bash with some other programming language. Variables get replaced, then what is left gets executed.
"$a"
This is the content of a, between quotation marks. a is empty, so this is equivalent to:
""
That is not a command. "Command not found." As there was an error, the execution was not successful (shell return code is not 0), so the second half of the command -- && echo 1 -- does not get executed.
Backticks...
``
...execute whatever is between them, with the output of that command replacing the whole construct. (There is also $() which does the same, and is less prone to being overlooked in a script.) So...
`echo "foo"`
...would evaluate to...
foo
...which would then be executed. So your...
``
...evaluates to...
<empty>
...which is then "executed successfully" (since there is no error).
If you want to test the contents of a, and execute echo 1 only if a is not empty, you should use the test command:
test -n "$a" && echo 1
There is a convenient alias for test, which is [, which also conveniently ignores a trailing ]...
[ -n "$a" ] && echo 1
...and a bash-ism [[ that "knows" about variable replacement and thus does not need quotation marks to avoid complaining about a missing argument if $a does indeed evaluate to empty...
[[ -n $a ]] && echo 1
...or, of course, the more verbose...
if [[ -n $a ]]
then
echo 1
fi
Ah. Missed the core part of the question:
$a && echo 1
This is two statements, separated by &&. The second statement only gets executed if the first one executes OK. The bash takes the line apart and executes the first statement:
$a
This is...
<empty>
...which is "successful", so the second statement gets executed. Opposed to that...
&& echo 1
...is a syntax error because there is no first statement. ;-) (Tricky, I know, but that's the way this cookie crumbles.)
a=""
or
a=" " #a long empty string
then
$> $a
will return 0
$> $noExistVar
will also return 0.
They get "executed", in fact, nothing gets executed. same as you press enter or pressing 10 spaces then enter, you get return code 0 too.
$> && echo 1
this will fail, because bash will try to execute the first part, in this case it is missing.
$> $notExistVar && echo 1
Here it works, I guess bash found the first part the $whatever, therefore no syntax error. Then "execute" it, well nothing to execute, return 0, (same as pressing enter after prompt), then check, if first part returned 0, exec the cmd after &&.
I said guess because I didn't check bash's source codes. Please correct me if it is wrong.
the $> " " && echo 1 case, I think it is clear, don't need to explain.

Shell script : How if condition evaluates true or false

What environment variable or something internally the 'if' keyword checks to decide true/false.
I am having something like below two statements. abc is mounted , but not pqr.
if mount |grep -q "abc"; then echo "export pqr"; fi
if mount |grep -q "pqr"; then echo "export abc"; fi
In the above case I expected first statement to do nothing since abc is mounted(so finds the row in mount o/p) hence the $? after mount |grep -q "abc" is 0.
And I expected second statement to execute the echo. But it's happening otherwise, first statement is printing but second not. So want to understand on what basis if decides true/false.
Here is one related question
But the accepted answer for that question says :
if [ 0 ]
is equivalent to
if [ 1 ]
If this is true then both my statements should do echo, right?
There is a basic difference between the commands that you are issuing and the analogy that you are drawing from the referenced question.
When grep is executed with the -q option, it exits with a return code of zero is the match is found. This implies that if the output of mount were to contain abc, then
if mount |grep -q "abc"; then echo "export pqr"; fi
is equivalent to saying:
if true; then echo "export pqr"; fi
Note that there is no test command, i.e. [, that comes into the picture here.
Quoting from the manual:
The test and [ builtins evaluate conditional expressions using a set
of rules based on the number of arguments.
0 arguments
The expression is false.
1 argument
The expression is true if and only if the argument is not null.
This explains why [ 0 ] and [ 1 ] both evaluate to true.
The if command does not act, like C-like languages, on the "boolean value" of an integer: it acts on the exit status of the command that follows. In shell, an exit status of 0 is considered to be success, any other exit status is failure. If the command following if exits with status 0, that is "true"
Example:
$ test -f /etc/passwd; echo $?
0
$ test -f /etc/doesnotexist; echo $?
1
$ if test -f /etc/passwd; then echo exists; else echo does not exist; fi
exists
$ if test -f /etc/doesnotexist; then echo exists; else echo does not exist; fi
does not exist
Note that [ and [[ are (basically) commands that (basically) alias test
if [ 0 ]
tests to see if the string 0 is non-empty. It is non-empty, so the test succeeds. Similarly, if [ 1 ] succeeds because the string 1 is non-empty. The [ command (also named test) is returning a value based on its arguments. Similarly, grep returns a value.
The if keyword causes the shell to execute commands based on the value returned by the command, but the output of the command preceded by if is irrelevant.
The command test 0 (equivalent to the command [ 0 ] returns a value of 0. The command test 1 also returns a value of 0. Zero is treated by the shell as a success, so the commands of the if clause are executed.

Resources