Is there a simple way to echo a message for a failure in a multi conditional if statement rather than keeping track of a flag or something.
Example is
if [ $i != 0 ] && (cat file.txt | grep -q sometext)
then
echo allGood
exit 0
else
echo either i is not 0 or file.txt doesnt contain sometext
exit -1
fi
I want to be more concise on which of the conditionals failed while remaining efficient if possible. So instead of the echo being one or other failed, in the if statement itself if first one fails I echo corresponding failure and then I just exit with exit status
Try something like
[ $i != 0 ] || echo i is zero && exit -1
grep -q sometext file.txt || echo sometext not found && exit -1
echo all good && exit 0
(note that I've also removed your UUOC)
Related
I have a wrapper script for a CI pipeline which works great, but it always returns with 0 even though subcommands in a for loop fails. Here is an example:
#!/bin/bash
file_list=("file1 file2 file_nonexistant file3")
for file in $file_list
do
cat $file
done
>./listfiles.sh
file1 contents
file2 contents
cat: file_nonexistant: No such file or directory
file3 contents
>echo $?
>0
Since the last iteration of the loop is successfull the entire script exits with 0.
What i want is for the loop to continue on fail and for the script to exit 1 if any of the loop iterations returned errors.
What i have tried so far:
set -e but it halts the loop and exits when an iteration fails
replaced done with done || exit 1 - no effect
replaced cat $file with cat $file || continue - no effect
Alternative 1
#!/bin/bash
for i in `seq 1 6`; do
if test $i == 4; then
z=1
fi
done
if [[ $z == 1 ]]; then
exit 1
fi
With files
#!/bin/bash
touch ab c d e
for i in a b c d e; do
cat $i
if [[ $? -ne 0 ]]; then
fail=1
fi
done
if [[ $fail == 1 ]]; then
exit 1
fi
The special parameter $? holds the exit value of the last command. A value above 0 represents a failure. So just store that in a variable and check for it after the loop.
The $? parameter actually holds the exit status of the previous pipeline, if present. If the command is killed with a signal then the value of $? will be 128+SIGNAL. For example 128+2 in case of SIGINT (ctrl+c).
Overkill solution with trap
#!/bin/bash
trap ' echo X $FAIL; [[ $FAIL -eq 1 ]] && exit 22 ' EXIT
touch ab c d e
for i in c d e a b; do
cat $i || export FAIL=1
echo F $FAIL
done
As all of you probably know, bash can print the last command exit code. This works for me, but I wanted to improve it by adding an if statement which checks if $? was 0. If it is 0 print the code in white, and if it is different, print it in red. Unfortunately this does not seem to work:
if [ $? == "0" ]; then
PS1=${PS1}'$(echo ${?})'
else
PS1=${PS1}'\e[1;31m\]$(echo ${?})'
fi
I also tried:
if [ $(echo $?) == "0" ]; then
PS1=${PS1}'$(echo ${?})'
else
PS1=${PS1}'\e[1;31m\]$(echo ${?})'
fi
also:
if [ $(echo ${?}) == "0" ]; then
PS1=${PS1}'$(echo ${?})'
else
PS1=${PS1}'\e[1;31m\]$(echo ${?})'
fi
None of them working.
Somehow variable is always 0, therefore printed in white.
How is it possible that I can print exit code, but cannot examine it with "if" ? Is this a bash limitation, or I am doing something wrong?
Just add $? to the PS1.
> PS1='PS $ '
> PS $ echo 1
> 1
> PS $ PS1='PS $? $ '
> PS 0 $ false
> PS 1 $ true
> PS 0 $
If you want to change color, you would have to do it inside PS1, not statically... Also note that $? will change it's value, so you need to save it.
PS1=${PS1}'$(ret=$?; if [ "$ret" -ne 0 ]; then printf "\e[1;31m\]"; fi; printf "%d" "$ret")'
$? is the exit status of the last command. If you execute some command, for instance [ … = … ], then $? changes. Example:
myCmd
echo $? # prints exit status of myCmd
echo $? # prints exit status of echo
myCmd
if [ $? = 0 ]; then
echo $? # prints exit status of `[`, here 0
else
echo $? # prints exit status of `[`, here 1
fi
Store the exit status of myCmd in a separate variable if you want to access it later.
myCmd
exitStatus=$?
echo $exitStatus # prints exit status of myCmd
echo $exitStatus # prints exit status of myCmd
By the way: "$(echo ${?})" is overly complicated; just "$?" is better in every way.
I have the unix shell script, in which i have given two condition for error and non error as shown below:
Script: Test.sh
#!/bin/sh
error1=ERROR
error2=FAIL
CheckError1=`grep $error1 proshell.sh | wc -l`
CheckErro2=`grep $error2 proshell.sh | wc -l`
if [ $CheckError1 -ge 1 ] then
exit 1
elif [ $CheckError2 -ge 1 ] then
exit 1
else
exit 0
fi
Note: The problem is that i am not getting always 1 for any errors for exit 1.
How to get only 1 for any errors?
You are checking the number of lines matched not the exit status of grep.
You want to do this:
error1=ERROR
error2=FAIL
file-proshell.sh
if grep -q "$error1" "$file" || grep -q "$error2" "$file"; then
exit 1
else
exit 0
fi
grep -q suppresses output, but exits with success exit status if a match was found
The exit statements in each status check if statement do not break the while loop and truly exit the script. Is there something I can do to break the loop and exit with that $STATUS code?
EDIT: I've updated my code and it still isn't working. The status check if statements successfully break the loop but when I try to evaluate the $EXIT_STATUS it's always null, likely having something to do with scope. What am I missing here?
if [ $RESTART -le $STEP ]; then
. tell_step
while read XML_INPUT; do
XML_GDG=`get_full_name $GDG_NAME P`
cp $XML_INPUT $XML_GDG
STATUS=$?
EXIT_STATUS=$STATUS
if [ $STATUS -ne 0 ]; then
break
fi
add_one_gen $XML_GDG
STATUS=$?
EXIT_STATUS=$STATUS
if [ $STATUS -ne 0 ]; then
break
fi
done < $XML_STAGE_LIST
echo $EXIT_STATUS
if [ $EXIT_STATUS -ne 0 ]; then
exit $EXIT_STATUS
fi
fi
I had the same problem: when piping into a while loop, the script did not exit on exit. Instead it worked like "break" should do.
I have found 2 solutions:
a) After your while loop check the return code of the while loop and exit then:
somecommand | while something; do
...
done
# pass the exit code from the while loop
if [ $? != 0 ]
then
# this really exits
exit $?
fi
b) Set the bash script to exit on any error. Paste this at the beginning of your script:
set -e
Not really understand why your script dosn't exits on exit, because the next is works without problems:
while read name; do
echo "checking: $name"
grep $name /etc/passwd >/dev/null 2>&1
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "grep failed for $name rc-$STATUS"
exit $STATUS
fi
done <<EOF
root
bullshit
daemon
EOF
running it, produces:
$ bash testscript.sh ; echo "exited with: $?"
grep failed for bullshit rc-1
exited with: 1
as you can see, the script exited immediatelly and doesn't check the "daemon".
Anyway, maybe it is more readable, when you will use bash functions like:
dostep1() {
grep "$1:" /etc/passwd >/dev/null 2>&1
return $?
}
dostep2() {
grep "$1:" /some/nonexistent/file >/dev/null 2>&1
return $?
}
err() {
retval=$1; shift;
echo "$#" >&2 ; return $retval
}
while read name
do
echo =checking $name=
dostep1 $name || err $? "Step 1 failed" || exit $?
dostep2 $name || err $? "Step 2 failed" || exit $?
done
when run like:
echo 'root
> bullshit' | bash testexit.sh; echo "status: $?"
=checking root=
Step 2 failed
status: 2
so, step1 was OK and exited on the step2 (nonexisten file) - grep exit status 2, and when
echo 'bullshit
bin' | bash testexit.sh; echo "status: $?"
=checking bullshit=
Step 1 failed
status: 1
exited immediatelly on step1 (bullshit isn't in /etc/passwd) - grep exit status 1
You'll need to break out of your loop and then exit from your script. You can use a variable which is set on error to test if you need to exit with an error condition.
I had a similar problem when pipelining. My guess is a separate shell is started when piplining. Hopefully it helps someone else who stumbles across the problem.
From jm666's post above, this will not print 'Here I am!':
while read name; do
echo "checking: $name"
grep $name /etc/passwd >/dev/null 2>&1
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "grep failed for $name rc-$STATUS"
exit $STATUS
fi
done <<EOF
root
yayablah
daemon
EOF
echo "Here I am!"
However the following, which pipes the names to the while loop, does. It will also exit with a code of 0. Setting the variable and breaking doesn't seem to work either (which makes sense if it is another shell). Another method needs to be used to either communicate the error or avoid the situation in the first place.
cat <<EOF |
root
yayablah
daemon
EOF
while read name; do
echo "checking: $name"
grep $name /etc/passwd >/dev/null 2>&1
STATUS=$?
if [ $STATUS -ne 0 ]; then
echo "grep failed for $name rc-$STATUS"
exit $STATUS
fi
done
echo "Here I am!"
In ksh shell, I wanna to check the return value after running a command, I've wrote two styles:
if [ $? -ne 0 ] ; then
echo "failed!"
exit 1
else
exit 0
fi
[ $? -ne 0 ] && echo "failed!" && exit 1
Are they equivalent? If not, what could I do if I wanna to write it in one line?
They're close, but not the same. First, the if will execute the exit 1 even if the echo failed for some reason; the chained expression won't. Also, the chained version lacks an equivalent of the else exit 0.
A better equivalent would be this:
[ $? -ne 0 ] && { echo "failed!"; exit 1; } || exit 0
This is tagged ksh, so you might find the numeric expression syntax cleaner:
(( $? )) && { echo "failed!"; exit 1; } || exit 0
But you can also write an if on one line, if you like:
if (( $? )); then echo "failed!"; exit 1; else exit 0; fi
If the command that you just ran above this expression in order to set $? is short, you may want to just use it directly as the if expression - with reversed clauses, since exit code 0 is true:
if grep -q "pattern" /some/filename; then exit 0; else echo "failed!"; exit 1; fi
It doesn't matter for this simple case, but in general you probably want to avoid echo. Instead, use printf - or if you don't mind being ksh-only, you can use print. The problem with echo is that it doesn't provide a portable way to deal with weird strings in variables:
$ x=-n
$ echo "$x"
$
While both printf and print do:
$ printf '%s\n' "$x"
-n
$ print - "$x"
-n
Again, not a problem here, or any time you're just printing out a literal string, but I found it was easier to train myself out of the echo habit entirely.