Bash pipes and exit problem PDF Print E-mail


In bash piping command lead to implicit place it subshells, what could lead unexpected behaviour if you wait it should terminate script with exit.

 

I have one simple bash helper function assert bored from http://tldp.org/LDP/abs/html/debugging.html and slightly modified:

#################################################################
# $1 - assertion to test
# $2 - (opt) - text of error to add in output if assertion failed
function assert ()# If condition false, exit from script
{                 #+ with appropriate error message.
E_ASSERT_FAILED=99

if [ ! $1 ]; then
echo "Assertion failed:  \"$1\". $2"
echo "Caller (line, file): $( caller )"
exit $E_ASSERT_FAILED
fi
}
#################################################################

All look fine and I start use it.

But there also not without submarine riffs.

It suddently stop working and hopefully I occasionally found error messages in logs. Assert stop working in strange way - error printed, but no script termination happened. I start dig and found very interesting behaviour:

echo 'Before function'
#assert # Exit happened
assert | tee -a LOG # No exit, next statement will be executed
echo 'After function'

I have spent many time to figure out what happened. In short - pipe run in subshell!

It may be not so obviously, but it stated in bash man (you could see explanation also there http://steve-parker.org/sh/functions.shtml ). There no standard way to terminate parent (main script) in this case regardless of used "return" or "exit" statements in function:http://stackoverflow.com/questions/4419952/difference-between-return-and-exit-in-bash-functionshttp://stackoverflow.com/questions/1816824/exit-entire-program-from-a-function-call-in-shell. Exception from that I found solution kill himself like "kill $$" - http://www.unix.com/shell-programming-scripting/138182-functions-exit-kill-bash.html. It may work in this particular solution, but also have some downsides like unavailable pass status code, may kill not intended script when it sourced and so on. Another way to do that job is explicitly check $PIPESTATUS special variable (More examples and details:http://unix.derkeiler.com/Newsgroups/comp.unix.shell/2005-06/1079.html). So, to provide also exit code from assert I wrote this small helper function:

# $1 - pipestatus code
function pipe_check_exit(){
[ $1 -gt 0 ] && exit $1
}

Example of usage:

assert '1 -gt 2' | tee LOG
pipe_check_exit ${PIPESTATUS: -1}
Share/Save/Bookmark
Written by Павел Алексеев aka Pahan-Hubbitus   
Sunday, 12 December 2010 21:19
Last Updated on Sunday, 09 January 2011 02:03
 

Add comment


Security code
Refresh