Bash - [: =: unary operator expected during trap execution - bash

I am trying to use trap in order to run a clean-app jar in case the main app jar exists successfully (0 exit code) :
trap "exit_code=$?; if [ "${exit_code}" = "0" ]; then java -jar /clean-app.jar; fi" EXIT
java -jar /main-app.jar
but I am getting the following error and not sure I'm getting the reason behind it :
/bin/bash: line 1: [: =: unary operator expected
Could someone share a pointer please ? Thank you

You can't nest double quotes like this. Here, you can switch to single quotes for the outer layer. You'll want to do this anyway because you need to prevent $? from being expanded before trap is called.
trap 'exit_code=$?; if [ "${exit_code}" = "0" ]; then java -jar /clean-app.jar; fi' EXIT
Better yet, define a function first.
handler () {
exit_code=$?
if [ "$exit_code" = 0 ]; then java -jar /clean-app.jar; fi
}
trap handler EXIT
In either case, you don't really need exit_code, as you don't need the original exit code after the test. You can also use -eq, since $? is guaranteed to be an integer.
handler () {
[ $? -eq 0 ] && java -jar /clean-app.jar
}

Related

How to make the sh script return an error code from the executed command?

I wrote a simple script which will prompt pppd to work. I have read in the pppd documentation that it can return an exit code in the range 1-20. I would like my script to return the value of pppd if it is not 0. I tried this way but unfortunately it failed.
myscript.sh:
#!/bin/sh
exec pppd call ${#:-provider}
if [ $? !-eq 0 ]
then
exit $?
fi
How could i get the exit code from pppd?
Just do:
#!/bin/sh
pppd call "${#:-provider}" || exit
...
or
if pppd call "${#:-provider}"; then : ; else exit; fi
If pppd fails, the script will exit and the value it returns will be that returned by pppd. (You could explicitly write exit $?, but that is the default value returned by exit when no argument is given and is not necessary.) If pppd succeeds, the script will continue.
The problem with your script (other than the ill-advised usage of exec, which I will mostly ignore) is that calling [ $? -ne 0 ] resets $?. You could re-write your script as:
#!/bin/sh
pppd call "${#:-provider}"
save_val=$?
if ! [ $save_val -eq 0 ]
then
exit $save_val
fi
but that seems excessively verbose.
Note that in your original script, you would get an error on the line if [ $? !-eq 0 ], since !-eq is not a valid operator. However, you never see that error because your early invocation of exec makes it so that line is never executed.

How to return from sourced bash script automatically on any error?

I have a bash script which is only meant to used be when sourced.
I want to return from it automatically on any error, similar to what set -e does.
However setting set -e doesn't work for me because it will also exit the users shell.
Right now I'm handling returning manually like this command || return 1, for each command.
You can also use command || true or command || return.
If your requirement is something different, please update more precisely.
You can use trap. E.g.:
// foo.sh
function func() {
trap 'if [ $? -ne 0 ]; then echo "Trapped!"; return ; fi' DEBUG
echo 'foo'
find -name "foo" . 2> /dev/null
echo 'bar'
}
func
Two notes. First, the trap needs to be inside the function as shown. It won't work if it's just inside the script.
Two, there is a significant limitation. Even if you set the return to the trap (e.g., return 1), while func exists after the bad find command, $? is still zero, no matter what. I'm not sure if there's a way around that, so if it's important to preserve the exit value of the failed command, this may not work.
E.g., if you had:
func
func_return=$?
echo "return value is: $func_return"
func_return will always be zero. I've played around with trying to get the exit value of the failed command to pass out of the function trap and into the function exit value, but have not found a way to do it.
If you need to preserve the return value, you could update a global variable inside the debug trap.
If I understand well, you can set -e locally in each function.
cat sourced
f1 () {
local -
set -e
[ "$1" -eq "$1" ] 2> /dev/null && echo "$1"
}
cat script.sh
. sourced
param='bad'
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"
param=3
ret=$(f1 "$param")
[ $? -eq 0 ] && echo "result = $ret" || \
echo "error in sourced file with param $param"

Bash script returning syntax error: syntax error in expression (error token is...)

I want to start a particular set of services (in the example below, the service is called users), and in my script, that is working. However, I see the following error when running the command:
./services.sh: line 40: [[: start users: syntax error in expression (error token is "users")
I am calling this command using a case statement in my script, that looks for the start parameter:
case "$1" in
(-h)
display_help
exit 0
;;
(start)
start_services "$#"
;;
My function for start_services is as follows:
start_services()
{
# Check to see if there are any arguments, if not, start all services
if [[ $# -eq 0 ]]; then
echo
echo "Starting all services:"
echo "======================"
for svc in $services
do
echo " Starting the $svc service... "
$SVCPATH/bin/$svc &
done
else
shift;
echo
for var in "$#"
do
echo "Starting the $var service... "
$SVCPATH/bin/$var & > /dev/null
done
fi
}
So, the failure is occurring at this line, line 40:
if [[ $# -eq 0 ]]; then
As I mentioned, the script is doing what I want, but I keep getting the syntax expression error.
Any ideas as to why this may be happening? Thanks in advance!
$# seems to contain your services names, but in if you try to count them. Use $# instead of $#.
Try:
if [[ -z "$#" ]]
-z: check for none zero string; faster than counting how many items there

Bash: return code in if statement causes unexpected behavior

I'm using the using the cmp command to check if two files are identical. I would like to stick the command into a one line if statement but it's not working as expected. When comparing two files and one the files doesn't exist, cmp returns 2. Since 2 is non-zero, I would expect the if statement to evaluate to true, but it does not and I don't understand why.
I want to write something like:
if cmp -s tickets.txt tickets_orig.txt; then
#do stuff here
fi
because I find it short, sweet and more intuitive. Is this possible?
I was able to create a workaround by using $?, but I don't understand why the code below will evaluate to true and the code above will evaluate to false when the command returns 2:
cmp -s tickets.txt tickets_orig.txt
if [ $? -ne 0 ]; then
#do stuff here
fi
Exit status ($?) = 0 means success in shell logic, it allows to define different error codes 1 to 127 for anormal status
if command; then #...
is equivalent to
command;
if [ $? -eq 0 ]; then #...
and
command;
if [ $? -ne 0 ]; then #...
equivalent to
if ! command; then #...

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