tee' and exit status
Here's an eet
. Works with every Bash I can get my hands on, from 2.05b to 4.0.
#!/bin/bash
tee_args=()
while [[ $# > 0 && $1 != -- ]]; do
tee_args=("${tee_args[@]}" "$1")
shift
done
shift
# now ${tee_args[*]} has the arguments before --,
# and $* has the arguments after --
# redirect standard out through a pipe to tee
exec | tee "${tee_args[@]}"
# do the *real* exec of the desired program
exec "$@"
(pipefail
and $PIPESTATUS
are nice, but I recall them being introduced in 3.1 or thereabouts.)
Exit status of tee (...)
Change your script to:
Command1 | tee >(grep sth || Command2)
to achieve the desired result.
A word about Subshells
>(....)
is a subshell. Anything and everything that you do within that subshell (except for the exit status of said subshell) is completely isolated from the outside world: (a=1); echo $a
will never echo the number 1
, because a
only has meaning within the subshell where it is defined.
I don't entirely understand why, but when you do redirection to a subshell, it seems to invert the exit status of that subshell, so that a failure will return true
and a success will return false
.
echo 'a' >(grep 'b') && echo false
# false
(exit 1) || echo false
# false
So if my first suggestion isn't working out for you, then try re-writing your script thusly:
Command1 | tee >(grep sth) && Command2
An example
a=1 # `a` now equals `1`
# if I run `exit`, $a will go out of scope and the terminal I'm in might exit
(exit) # $a doesn't go out of scope because `exit` was run from within a subshell.
echo $a # $a still equals `1`
Where you can go to learn more about subshells
Set a parent shell's variable from a subshell
Pass variable from a child to parent in KSH
Variables value gets lost in subshell
http://www.tldp.org/LDP/abs/html/subshells.html
http://mywiki.wooledge.org/SubShell
http://wiki.bash-hackers.org/syntax/expansion/proc_subst
In bash how do I exit a script from a function that is piped by tee?
If you create a pipeline, the function is run in a subshell, and if you exit
from a subshell, only the subshell will be affected, not the parent shell.
printPid(){ echo $BASHPID; }
printPid #some value
printPid #same value
printPid | tee #an implicit subshell -- different value
( printPid ) #an explicit subshell -- also a different value
If, instead of aFunction | tee
you do:
aFunction > >(tee)
it'll be essential the same, except aFunction
won't run in a subshell, and thus will be able to affect the current environment (set variables, call exit, etc.).
Piping command output to tee but also save exit code of command
Since you're running bash
, you can use its $PIPESTATUS variable instead of $?
:
mvn clean install $@ | tee $logfile
echo ${PIPESTATUS[0]}
Tee resets exit status is always 0
Well, this is something that, for some peculiar reason, the docs don't mention. The code, however, does:
int wait_for (pid) { /*...*/
/* If this child is part of a job, then we are really waiting for the
job to finish. Otherwise, we are waiting for the child to finish. [...] */
if (job == NO_JOB)
job = find_job (pid, 0, NULL);
So it's actually waiting for the whole job, which, as we know, normally yields the exit status of the last command in the chain.
To make matters worse, $PIPESTATUS
can only be used with the last foreground command.
You can, however, utilize $PIPESTATUS
in a subshell job, like this:
(<some_process> | tee -a /tmp/some.log; exit ${PIPESTATUS[0]}) &
# somewhere down the line:
wait %%<some_process>
How to exit an if clause
(This method works for if
s, multiple nested loops and other constructs that you can't break
from easily.)
Wrap the code in its own function. Instead of break
, use return
.
Example:
def some_function():
if condition_a:
# do something and return early
...
return
...
if condition_b:
# do something else and return early
...
return
...
return
if outer_condition:
...
some_function()
...
Pipe output and capture exit status in Bash
There is an internal Bash variable called $PIPESTATUS
; it’s an array that holds the exit status of each command in your last foreground pipeline of commands.
<command> | tee out.txt ; test ${PIPESTATUS[0]} -eq 0
Or another alternative which also works with other shells (like zsh) would be to enable pipefail:
set -o pipefail
...
The first option does not work with zsh
due to a little bit different syntax.
Related Topics
How to Create a Bash Variable Like $Random
How Kernel Notify a User Space Program an Interrupt Occurrs
Old Logs Are Not Imported into Es by Logstash
Shell Script Issue with Filenames Containing Spaces
Sed: Find Pattern Over Two Lines, Not Replace After That Pattern
Start Docker-Compose Automatically on Ec2 Startup
Hard Time Installing Ia32-Lib on MAC Osx
Wc -M in Unix Adds One Character
Get Process Executed by Mono on Gnu/Linux
Linux Perf Record: Difference Between Count (-C) and Frequency (-F) Options
Add Month to a Variable Date in Shell Script
Multiple -A with Greater Than/Less Than Break Bash Script
How Provide Nested Mount of Overlayfs