someCommand 2>&1 | grep pattern &
How do I get the exit status of someCommand
?
PIPESTATUS
doesn't work because it's a background process.
I've found this link and it seems to work but only if I use it exactly that way. Simple echo
ing to the screen doesn't seem to work. I was wondering if it's possible to get the exit code without creating temporary files.
In bash you could do :
echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
Example :
$ ls -l | grep somefile
-rw-rw-r-- 1 me me 32 May 4 15:47 somefile
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
0 0
$ ls -l 1>/dev/null | grep while
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
0 1
For piped foreground processes
In case of a script say testscript.sh which contains :
#!/bin/bash
echo "Some Stuff"
exit 29 # Some random exit code for testing
do
$./testscript.sh | grep somestuff
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
29 1
For piped background processes
Method 1: Using pipefail
For testscript.sh which contains :
#!/bin/bash
set -eo pipefail
#set -o pipefail causes a pipeline to produce a failure return code
#If a command fails, set -e will make the whole script exit,
cat nonexistingfile # this command fails
echo "Some Stuff"
exit 29
Do
$ ./testscript.sh 2>/dev/null | grep Some &
[2] 7684
$ fg 2
bash: fg: job has terminated
[2]- Exit 1 ./testscript.sh 2> /dev/null | grep --color=auto Some
You get an the exit status 1 from which you conclude that the script failed.
Had cat nonexistingfile
been removed you would have got:
[2]- Done ./37257668.sh 2> /dev/null | grep --color=auto Some
Disdvantage : pipefail will return a one for all exit code that is not specific to the command that failed
Method 2 : Source the shell script
$ . ./testscript.sh 2>/dev/null | grep Some & #mind the dot in the beginning
$ echo "${PIPESTATUS[0]} ${PIPESTATUS[1]}"
29 0
Final Touch
If you suspect a single command to fail in a shell script,test script, you could do below :
#no shebang
echo "Some Stuff"
ls non_existent 2>/dev/null || ls__return_value=50
Do
$. ./testscript | grep "Some"
$if [ $ls__return_value -eq 50 ]; then echo "Error in ls"; fi
With a temp file
You can put the code that saves the content of PIPESTATUS in a temp file inside { ... }
and run it in background. This approach would be needed if we are interested in the exit code of more than one command in the pipeline:
{
someCommand 2>&1 | grep -- pattern
printf "0=%s\n1=%s\n" "${PIPESTATUS[0]}" "${PIPESTATUS[1]}" > status.out
} &
wait "$!"
# grab the exit code from status.out file
Without a temp file
{
someCommand 2>&1 | grep -- pattern
exit ${PIPESTATUS[0]}
} &
wait "$!"
echo $? # this is the exit code of someCommand
wait: wait [n]
Wait for the specified process and report its termination status. If
N is not given, all currently active child processes are waited for,
and the return code is zero. N may be a process ID or a job
specification; if a job spec is given, all processes in the job's
pipeline are waited for.