4

I'm trying to understand why whenever I'm using function 2>&1 | tee -a $LOG tee creates a subshell in function that can't be exited by simple exit 1 (and if I'm not using tee it works fine). Below the example:

#!/bin/bash
LOG=/root/log.log

function first()
{
echo "Function 1 - I WANT to see this."
exit 1
}

function second()
{
echo "Function 2 - I DON'T WANT to see this."
exit 1
}
first 2>&1 | tee -a $LOG
second 2>&1 | tee -a $LOG

Output:

[root@linuxbox ~]# ./1.sh  
Function 1 - I WANT to see this.
Function 2 - I DON'T WANT to see this.

So. if I remove | tee -a $LOG part, it's gonna work as expected (script will be exited in the first function).

Can you, please, explain how to overcome this and exit properly in the function while being able to tee output?

4ae1e1
  • 7,228
  • 8
  • 44
  • 77
LinenG
  • 109
  • 3
  • 14
  • "creates a subshell in function" Well, you already said that. Why? Because all simple commands in a pipeline except the last one are executed in subshells (the last one depends on an option or something — I forgot). Why? Because. As for how to exit properly, check exit status (if `pipefail` is set) or `PIPESTATUS` after the pipeline. – 4ae1e1 Dec 20 '15 at 21:15
  • Follow-up on comment above: the option I forgot is `lastpipe`. – 4ae1e1 Dec 20 '15 at 21:21
  • Related: http://stackoverflow.com/q/9277827/951890 – Vaughn Cato Dec 20 '15 at 21:29
  • 4ae1e1, Thank you too. As with a help of you pointer I also learned about PIPESTATUS, and apparently the workaround: `function PIPE_STAT() { if [[ $PIPESTATUS -ne 0 ]]; then exit 1 fi }` Can be used and placed after each `function 2>&1 | tee -a $LOG` to verify the status and exit if needed. – LinenG Dec 20 '15 at 21:31

3 Answers3

3

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.).

Petr Skocik
  • 58,047
  • 6
  • 95
  • 142
2

Use PIPESTATUS to retrieve the exit status of the first command in the pipeline.

first 2>&1 | tee -a $LOG; test ${PIPESTATUS[0]} -eq 0 || exit ${PIPESTATUS[0]}
second 2>&1 | tee -a $LOG; test ${PIPESTATUS[0]} -eq 0 || exit ${PIPESTATUS[0]}
chepner
  • 497,756
  • 71
  • 530
  • 681
1

You can tell bash to fail if anything in the pipeline fails with set -e -o pipefail:

$ cat test.sh
#!/bin/bash
LOG=~/log.log

set -e -o pipefail

function first()
{
echo "Function 1 - I WANT to see this."
exit 1
}

function second()
{
echo "Function 2 - I DON'T WANT to see this."
exit 1
}
first 2>&1 | tee -a $LOG
second 2>&1 | tee -a $LOG
$ ./test.sh
Function 1 - I WANT to see this.
Ewan Mellor
  • 6,747
  • 1
  • 24
  • 39