Syntax help in shell scripting - syntax

I am a newbee in shell scripting and am learning it from online tutorial website .
In a particular code I came across the following line :
[ -z "${starkisland}" ] && { echo "`basename $0`: ERROR: starkisland environment variable not defined !" ; exit 2 ; }
Here I see [ -z "${starkisland}" ] is an if condition meaning if the file starkisland is of zero size and the part after && is like the part that is to be executed if the condition is true. But I guess && is the symbol for AND condition, if I am not wrong.
Can anyone help me in understanding the statement and logic behind writing like this if && is the AND condition.?

You should read this
if [ CONDITION ] && {ACTION1} || {ACTION2}
as
If CONDITION then ACTION1 else ACTION2
In fact these are not logical operators, these flow control operators
&& means "execute if exit code is 0"
|| means "execute if exit code is not 0"

Related

How do I update a "global" variable inside a while loop?

So I have this If statement that checks what the exit code of the script is:
if [ $? -eq 0 ]; then
wasSuccessful=true;
fi
In other languages, I would do something like:
while wasSuccessful == false
And then the loop would keep running until the exit code was true. (I plan to implement a loop counter as well for too many failed attempts, but that's a different problem to solve)
I think I need a here-string, but I'm not exactly sure how that would look. Here's a rough outline of my code right now, in Bash:
wasSuccessful=false
while [ "$wasSuccessful" = "false" ]
do
#Bunch of code here, then the check for exit code
if [ $? -eq 0 ]; then
wasSuccessful=true;
fi
done
Any suggestions on how to do something like this would be much appreciated :)
The question is how to exit while loop when the latest command execute successfully inside while loop?
You don't need a global variable to check. Just check the status of previous command and break it if succeed.
while true
do
# a bunch of code here
[ $? -eq 0 ] && break
done

what does [ -n "$VARIABLE" ] || exit 0 mean

Looking at correcting an issue in /etc/init.d/hostapd on Debian. However, I have no clue what this line of code does nor how it works
[ -n "$DAEMON_CONF" ] || exit 0
In searching online for bash tutorials, I've never seen anyone do this
When I run the code, my shell window closes (because $DAEMON_CONF is not set to anything). If I change the code to
[ -n "not empty" ] || exit 0
my console window does not close.
so, -n evaluates to true, and or'ed with exit 0, is what?
If the expression in [] returns false, do the thing after the or || (and exit 0). Otherwise, it will short circuit and the next statement will be evaluated.
[ is and alternate name for the command test. You can learn about the parameters/flags whatnot by looking at test's manpage:
man test
You'll see for -n:
-n STRING
the length of STRING is nonzero
Furthemore || means OR. So if the test command returns False then the stuff after the || will be executed. If test returns true, then it won't be executed.
Written out your command says: "If the variable $DAEMON_CONF lacks a value, then exit with return code 0"
The longhand version would be something like:
if test ! -n "$DAEMON_CONF"; then
exit 0
fi
[ -n "$DAEMON_CONF" ] || exit 0
It's an unnecessary double negative. This would do the same thing:
[ -z "$DAEMON_CONF" ] && exit 0
Or it could be done without any flag:
[ "$DAEMON_CONF" ] || exit 0
It checks if the environment variable is defined, if $DAEMON_CONF is not present the it will exit with 0 code, a better code would be.
[ -n "$DAEMON_CONF" ] || echo "exiting as DAEMON_CONF is not set" && exit 1

retrieve error code from a command launched within a bash script

Ok I'm kind of new to bash scripting [the advanced stuff] and I need a little help. I don't even know exactly how to phrase this so I'll just explain what I am doing and what I need to know about it.
in my script I run a ./configure and I need to be able to catch if there was an error in the configure and react accordingly within the bash script.
the code is:
function dobuild {
echo -e "\e[1;35;40mExecuting Bootstrap and Configure\e[0m"
cd /devel/xbmc
if [ $Debug = "1" ];
then
#either outputs to screen or nulls output
./bootstrap >/dev/null
/usr/bin/auto-apt run ./configure --prefix=/usr --enable-gl --enable-vdpau --enable-crystalhd --enable-rtmp --enable-libbluray >/dev/null
else
./bootstrap
/usr/bin/auto-apt run ./configure --prefix=/usr --enable-gl --enable-vdpau --enable-crystalhd --enable-rtmp --enable-libbluray
fi
}
and say the configure returns an error 1 or 2 how do I trap that and act on it?
TIA
After the execution of every shell command it's return value, a number between 0 and 255, is available in the shell variable ?. You can get the value of this variable by prefixing it with the $ operator.
You have to be a little careful with ?, because it is reset by every command, even a test. For example:
some_command
if (( $? != 0 ))
then
echo "Error detected! $?" >&2
fi
Gives: Error detected! 0 because ? was reset by the test condition. It is probably best to store ? in another variable if you are going to use it later, which includes doing more than one test on it.
To do a numeric test in bash use the (( ... )) numeric test construct:
some_command
result=$?
if (( $result == 0 ))
then
echo "it worked!"
elif (( $result == 1 ))
then
echo "Error 1 detected!" >&2
elif (( $result == 2 ))
then
echo "Error 2 detected!" >&2
else
echo "Some other error was detected: $result" >&2
fi
Alternatively use a case statement.
After the execution of a command, the returned value is stored in the shell variable $?. So you would have to match that with the return values of success and failure
if [ $? == 1 ]
then
#do something
else
#do something else
fi
The other answers about $? are great (though be careful about assuming values other than 0 and not-0 - different commands. or different versions of the same command may fail with different values), but if you just need to act on success or failure immediately, you can simplify things:
if command ; then
# success code here
else
# failure code here
fi
Or if you only want to act on failure, here's a hack for older shells (the colon is a null command but it satisfies the then clause):
if command ; then :
else
# failure code here
fi
But in modern shells like bash this is better:
if ! command ; then # use the ! (not) operator
# failure code here
fi
And, if you only need to do simple things, you can use the "short circuit" operators:
command1 && command2_if_command1_succeeds
command1 || command2_if_command1_fails
Those only work for single commands, stringing more && and || on them doesn't do what you might think in most cases so most people avoid that. However, you can do multiple commands if you group them:
command1 && { command2; command3; command4; }
That can get hard to read so it's best to keep it simple if you use it all:
command1 || { echo "Error, command1 failed!" >&2; exit 1; }

bash one-line conditional fails when using set -e

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.

Exception handling in shell scripting?

I'm looking for exception handling mechanism in shell script. Is there any try,catch equivalent mechanism in shell script ?
There is not really a try/catch in bash (i assume you're using bash), but you can achieve a quite similar behaviour using && or ||.
In this example, you want to run fallback_command if a_command fails (returns a non-zero value):
a_command || fallback_command
And in this example, you want to execute second_command if a_command is successful (returns 0):
a_command && second_command
They can easily be mixed together by using a subshell, for example, the following command will execute a_command, if it succeeds it will then run other_command, but if a_command or other_command fails, fallback_command will be executed:
(a_command && other_command) || fallback_command
The if/else structure and exit codes can help you fake some of it. This should work in Bash or Bourne (sh).
if foo ; then
else
e=$? # return code from if
if [ "${e}" -eq "1"]; then
echo "Foo returned exit code 1"
elif [ "${e}" -gt "1"]; then
echo "Foo returned BAD exit code ${e}"
fi
fi
{
# command which may fail and give an error
} || {
# command which should be run instead of the above failing command
}
Here are two simple bashfunctions which enable eventhandling in bash:
You could use it for basic exceptionhandling like this:
onFoo(){
echo "onFoo() called width arg $1!"
}
foo(){
[[ -f /tmp/somefile ]] || throw EXCEPTION_FOO_OCCURED "some arg"
}
addListener EXCEPTION_FOO_OCCURED onFoo
Exceptionhandling using try/catch blocks is not supported in bash, however, you might wanna try looking at the BANGSH framework which supports it (its a bit like jquery for bash).
However, exceptionhandling without cascading try/catch-blocks is similar to eventhandling, which is possible in almost any language with array-support.
If you want to keep your code nice and tidy (without if/else verbosity), I would recommend to use events.
The suggestion which MatToufoutu recommends (using || and &&) is not recommended for functions, but ok for simple commands. (see BashPitfalls about the risks)
Use following to handle error properly where error_exit is function that accepts one argument. In case if argument is not passed then it will throw unknown error with LineNo where actually error is happening. Please experiment before actually uses for production -
#!/bin/bash
PROGNAME=$(basename $0)
error_exit()
{
echo "${PROGNAME}: ${1:-"Unknown Error"}" 1>&2
exit 1
}
echo "Example of error with line number and message"
error_exit "$LINENO: An error has occurred."

Resources