How to Check Exit If Used Tee

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 ifs, 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



Leave a reply



Submit