So in my bash script, I output status report to terminal as well as write it to the log file. I wanted to use a bash ternary operator that will output to terminal as well as write a log file if variable LOG_TO_TERMINAL is true, and if that is set to false, just write to a log file without outputting status to the terminal.
My sample code looks like this:
[[ $LOG_TO_TERMINAL ]] && echo "error message" >> $LOG_FILE || echo "error message" | tee -a $LOG_FILE
which just logs the file instead of echoing to the terminal no matter whether I set LOG_TO_TERMINAL to true or false.
To isolate the problem, I tried simplifying the code to:
[[ $LOG_TO_TERMINAL ]] && echo "log to terminal" || echo "don't log to terminal"
But this code snippet also echoes "log to terminal" no matter what its value is.
The test [[ $LOG_TO_TERMINAL ]] tests whether LOG_TO_TERMINAL has a value or not. Nothing else. The shell doesn't treat false (or 0 or null etc.) as special false-y values.
If you want some other test you need to test specifically for that.
[[ $LOG_TO_TERMINAL = true ]]
or
[[ $LOG_TO_TERMINAL != false ]]
or
[[ $LOG_TO_TERMINAL = 1 ]]
etc.
If you were expecting to use the return code from the true and/or false commands then you need $LOG_TO_TERMINAL && Y || Z or similar to run the command stored in the variable (though I wouldn't recommend this version of this test).
Also note that X && Y || Z is not a ternary operation in the shell. See the Shellcheck wiki for warning SC2015 for more about this.
You want this:
[[ $LOG_TO_TERMINAL = 1 ]] && echo "log to terminal" || echo "don't log to terminal"
Related
So I'm having a bit of a problem when running this command in bash;
echo "$usr_age" | grep "^[0-9]*$" > $null || echo "Please only use numbers in the Age field." || exit 1
When running it, if the "$usr_age" variable has anything that is not a number, it warns the user, but it doesn't exit the script. I also tried changing the last || to && but if I do so it will just exit the script even if the variable is all numbers.
Note: the "$null" variable is just "/dev/null"
Thank you.
Use a grouping operator to combine the echo and the exit. a || b runs b only if a fails, whereas you want to run exit whether or not echo succeeds.
grep -q "^[0-9]*$" <<<"$usr_age" || { echo "Only use numbers in the Age field."; exit 1; }
By the way -- grep, as an external command, is quite slow to start up compared to using a shell builtin. Consider instead bash's built-in regex support:
[[ $usr_age =~ ^[0-9]*$ ]] || { echo "Only use numbers in the Age field."; exit 1; }
Assuming thoses functions :
return_0() {
return 0
}
return_1() {
return 1
}
Then the following code :
if return_0; then
echo "we're in" # this will be displayed
fi
if return_1; then
echo "we aren't" # this won't be displayed
fi
if return_0 -a return_1; then
echo "and here we're in again" # will be displayed - Why ?
fi
Why I am getting into the last ifstatement ?
Aren't we supposed to be out of the condition with those 0 and 1 ?
-a is one of the options of the test command (which is also implemented by [ and [[). So you can't just use -a by itself. You probably want to use &&, which is a control operator token for an AND list.
if return_0 && return_1; then ...
You can use -a to tell test to "and" two different test expressions, like
if test -r /file -a -x /file; then
echo 'file is readable and executable'
fi
But this is equivalent to
if [ -r /file -a -x /file ]; then ...
which may be more readable because the brackets make the test part of the expression clearer.
See the Bash Reference Manual for further information on...
&&, see lists
if statements and the various test commands and keywords, see conditional constructs
When you execute
if return_0 -a return_1; then
echo "and here we're in again" # will be displayed - Why ?
fi
You execute the line return_0 -a return_1. This actually means that you pass -a and return_1 as arguments to return_0. If you want to have an and operation, you should make use of the && syntax.
if return_0 && return_1; then
echo "and here we're in again" # will be displayed - Why ?
fi
The useful information to understand this is:
AND and OR lists are sequences of one of more pipelines separated by the && and || control operators, respectively. AND and OR lists are executed with left associativity. An AND list has the form
command1 && command2
command2 is executed if, and only if, command1 returns an exit status of zero.
An OR list has the form
command1 || command2
command2 is executed if and only if command1 returns a non-zero exit status. The return status of AND and OR lists is the exit status of the last command executed in the list.
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; }
I have a secondary list of start up programs that I would like to start in case I am working. So I have added this shell script to the start up of my system running on Ubuntu.
echo "Do you want to start the start up applications[Y/n]?"
while read inputline
do
what="$inputline"
break
done
if [ "$what" == "Y" -o "$what" == "y" ]
then
. ~/bin/webstorm.sh &
workrave &
firefox &
. ~/bin/AptanaStudio3
fi
I keep getting this error that says something like [: Y: unexpected operator all the time and never starts the programs.
Disclaimer: I have no idea how to write shell scripts.
Try changing:
if [ "$what" == "Y" -o "$what" == "y" ]
to
if [[ "$what" == "Y" ]] || [[ "$what" == "y" ]]
What is your shebang line? #!/bin/bash or #!/bin/sh?
If you are intending to use bash as script interpreter, don't forget shebang line.
(Your code is working with bash on my system at least..)
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.