I am learning Shell Scripting and i got stuck that in most languages like C,C++, 0 means false and 1 means true, but in below shell script I am not able to understand, how the output is generated
if [ 0 ]
then
echo "if"
else
echo "else"
fi
No matter what i write inside if block like instead of 0, I tried 1,2,true,false it is always running if condition. How this works in shell scripting.
And what shell script returns when the expression inside if statement is false.
It is always executing if part because this condition:
[ 0 ]
will always be true as it checks if the string between [ and ] is not null/empty.
To correctly evaluate true/false use:
if true; then
echo "if"
else
echo "else"
fi
There are no booleans in Bash.
But here are some examples that are interpreted falsy:
Empty value: ""
Program exiting with non-zero code
A 0 as in your example is not falsy, because it's a non-empty value.
An example with empty value:
if [ "" ]
then
echo "if"
else
echo "else"
fi
An example with non-zero exit code (assuming there's no file named "nonexistent"):
if ls nonexistent &> /dev/null
then
echo "if"
else
echo "else"
fi
Or:
if /usr/bin/false
then
echo "if"
else
echo "else"
fi
Or:
if grep -q whatever nonexistent
then
echo "if"
else
echo "else"
fi
Related
In bash, if I run
(foo=14)
And then try to reference that variable later on in my bash script:
echo "${foo}"
I don't get anything. How can I make bash store this variable the way I need it to?
Specifically, I am using this in an if statement and checking the exit code, something kind of like:
if (bar="$(foo=14;echo "${foo}"|tr '1' 'a' 2>&1)")
then
echo "Setting "'$bar'" was a success. It is ${bar}"
else
echo "Setting "'$bar'" failed with a nonzero exit code."
fi
Commands enclosed in parenthesis e.g. () are executed in a sub-shell. Any assignment in a sub-shell will not exist outside that sub-shell.
foo=14
bar=$(echo $foo | tr '1' 'a' )
if [[ $? -eq 0 ]]
then
echo "Setting "'$bar'" was a success. It is ${bar}"
else
echo "Setting "'$bar'" failed with a nonzero exit code."
fi
I have the following unix shell script, in which i have two integer
variables namely a and b.
If a is greater then or equal to b then shell script should exit with returning 0.
Else it should exit with returning 1.
My try:
Script: ConditionTest.sh
#!/bin/sh
a=10
b=20
if [ $a -ge $b ]
then
exit 0
else
exit 1
fi
....
....
....
Running Script:
$ ./ConditionTest.sh
$
Note: I am not getting any return value after executing the file.
The shell puts the exit status of the last command in the variable ?.
You could simply inspect it:
mycommand
echo $?
... or you could use it to do something else depending on its value:
mycommand && echo "ok" || echo "failed"
or alternatively, and slightly more readable:
if mycommand; then
# exit with 0
echo "ok"
else
# exit with non-zero
echo "failed"
if
Your script looks fine; you did everything right.
#!/bin/sh
a=10
b=20
if [ $a -ge $b ]
then
exit 0
else
exit 1
fi
So here's where we run it and check the return value:
$ sh test.sh
$ echo $?
1
$
10 is not greater than or equal to 20.
Another way to test it would be like this:
$ sh test.sh && echo "succeeded" || echo "failed"
failed
As noted in the comments, you should also quote your variables, always:
if [ $a -ge $b ]
Should be:
if [ "$a" -ge "$b" ]
To add to the previous answers, the key idea you should understand is that every program provides a number when exiting. That number is used as a way to report if the command has completed its operation successfully, and if not, what type of error has occurred.
Like mentioned, the exit code of the last command executed can be accessed with $?.
The reason nothing was printed by your script, is that your script returned 1, but the exit code of a command is not printed. (This is analogous to calling a function, you get a return value from the function but it's not printed)
how to make this work?
#!/bin/bash
# this code is ugly and does not work
check_if_failed() {
echo "arg1: $1"
echo "arg2: $2"
if [ $1 -ne 0 ] ; then
exit $1
fi
}
CHECK="check_if_failed $? $LINENO"
true ; $CHECK
false ; $CHECK
# (edit)
if true ; then
false ; $CHECK
fi
The goal is to have one very small command to check the returncode, so that I can append it to every command line in a simple way.
Why adding something after each command you want to check? Let bash do it for you!
#!/bin/bash
trap 'echo "Line $LINENO returned $?"' ERR
true
false || : # this will not be checked
false # this will be checked
I think what you're looking for is simply
some_command || exit
As in C, the || short-circuits, so its right-hand side only gets evaluated if the left-hand side evaluates to "false" (which is interpreted here as a nonzero return code).
I started using set -e in my bash scripts,
and discovered that short form of conditional expression breaks the script execution.
For example the following line should check that $var is not empty:
[ -z "$var" ] && die "result is empty"
But causes silent exit from script when $var has non-zero length.
I used this form of conditional expression in many places...
What should I do to make it run correctly? Rewrite everything with "if" construction (which would be ugly)? Or abandon "set -e"?
Edit: Everybody is asking for the code. Here is full [non]working example:
#!/bin/bash
set -e
function check_me()
{
ws="smth"
[ -z "$ws" ] && echo " fail" && exit 1
}
echo "checking wrong thing"
check_me
echo "check finished"
I'd expect it to print both echoes before and after function call.
But it silently fails in the check_me function. Output is:
checking wrong thing
Use
[ -n "$var" ] || die "result is empty"
This way, the return value of the entire statement is true if $var is non-empty, so the ERR trap is not triggered.
I'm afraid you will have to rewrite everything so no false statements occur.
The definition of set -e is clear:
-e Exit immediately if a simple command (see SHELL GRAMMAR above) exits with a non-zero status. The shell does not exit if the command that fails is part of the command list immediately following a while or until keyword, part of the test in an if statement, part of a && or || list, or if the command's return value is being inverted via !. A trap on ERR, if set, is executed before the shell exits.
You are using the "optimization" system of Bash: because a false statement will cause an AND (&&) statement never to be true, bash knows it doesn't have to execute the second part of the line. However, this is a clever "abuse" of the system, not intended behaviour and therefore incompatible with set -e. You will have to rewrite everything so it is using proper ifs.
You should write your script such that no command ever exits with non-zero status.
In your command [ -z "$var" ] can be true, in which case you call die, or false in which case -e does it's thing.
Either write it with if, as you say, or use something like this:
[ -z "$var" ] && die "result is empty" || true
I'd recommend if though.
What the bash help isn't very clear on is that only the last statement in an && or || chain is subject to causing an exit under set -e. foo && bar will exit if bar returns false, but not if foo returns false.
So your script should work... but it doesn't. Why?
It's not because of the failed -z test. It's because that failure makes the function return a non-zero status:
#!/bin/bash
set -e
function check_me()
{
ws="smth"
[ -z "$ws" ] && echo " fail" && exit 1
# The line above fails, setting $? to 1
# The function now returns, returning 1!
}
echo "checking wrong thing"
check_me # function returns 1, causing exit here
echo "check finished"
So there are multiple ways to fix this. You could add ||true to the conditional inside the function, or to the line that calls check_me. But as others have pointed out, using ||true has its own problems.
In this specific scenario, where the desired postcondition of check_me is "either this thing is valid or the script has exited", the straightforward thing to do is to write it like that, i.e. [[ -n "$ws" ]] || die "whatever".
But using && conditions will actually work fine with set -e in general, as long as you don't use such a conditional as the last thing in a function. You need to add an explicit true or return 0 or even : as a statement following such a conditional, unless you intend the function to return false when the condition fails.
In bash script
if [ 1 ]
then
echo "Yes"
else
echo "No"
fi
Output: Yes
It represents that '1' is treated as true value.
But in code:
word = Linux
letter = nuxi
if echo "$word" | grep -q "$letter"
then
echo "Yes"
else
echo "No"
fi
Output: No
But echo "$word" | grep -q "$letter" will return 1, so why is the result is No.
How does the keyword if test the value returned by the command after if?
The return value of a command is checked. [ 1 ] has a return value of 0 (true). Any other return value (like 1) indicates an error.
You can display the return value of the last executed command using the $? variable:
true
echo $?
# returned 0
false
echo $?
# returned 1
echo $?
# returned 0 as the last executed command is 'echo', and not 'false'
In unix land, 0 is true and 1 is false.
For your first example:
if [ 1 ]
then
echo "Yes"
else
echo "No"
fi
"If" checks the exit code of the given command for true/false (i.e. zero/non-zero).
The square brackets actually invoke the "test" command (see "man test" for more information) and give the exit code to if.
"test 1" (or indeed "test any_string") returns true (0) so "Yes" is output.
For your second example, this outputs "No" because "nuxi" isn't found in "Linux", if you change "nuxi" to "nux" (perhaps this was a typo?) and remove the spaces around the = then you will get the behaviour you expect. e.g.
word=Linux
letter=nux
if echo "$word" | grep -q "$letter"
then
echo "Yes"
else
echo "No"
fi
This is because the grep failed to find the $letter in $word, hence the exit code is 1. Whenever a process in linux return a code other than 0 then it means it failed. 0 means exited successfully. You can verify this by echo "Linux" | grep -d "nuxi"; echo $?
On the other hand in scripting world 0 means false and 1 mean true. So the grep failed to find the word and send 1 as an exit code to if, which took it as a true value.