“Tee”-ing Off
In Unix, the Tee command is used to ‘T’, or split, the output of a stream into two identical streams. This can be useful for many things, such as writing to a file while also writing to stdout:
$whoami | tee /tmp/file ubuntu
This left us with a file that we can later use:
$cat /tmp/file ubuntu
Tee can also be used to debug a string of piped commands when you don’t want to disrupt the pipe:
cat console.log | grep 'config' | tee /tmp/file | sort -n | uniq -c
Now you can cat/tmp/file and see what was happening between grep and sort.
And Now For a Short Story
One day at the OpenDNS office, we notice that our Jenkins CI build server was reporting all builds as success — even though they were actually failing! If you’re new to Tee, it might not be immediately apparent what went wrong:
./build.sh | tee console.log
The problem here is that the pipe to Tee replaces the exit code of the command with that of Tee. Tee always exits 0 — it always successes. So, in order to catch the failure you can use PIPESTATUS in bash:
./build.sh | tee console.log; test ${PIPESTATUS[0]} -eq 0
Since we are calling index[0] of PIPESTATUS, this will give us the exit code of our build.sh script. Be aware that when using multiple pipes, you’ll need to check the status of each one to determine which pipe failed.
Finally, we’re using the command test -eq 0 to compare equality between the PIPESTATUS exit code and zero — if true, then that means our build script exited successfully this time.
If there are multiple commands happening and you’re still not getting the exit code you expect, then it might make sense to wrap everything in a bash script and use set -e, which will force the script to exit upon an non-zero exit code. Just remember that set -e has some gotchas, and it’s probably better to use a trap to catch the signal and exit appropriately.