Bash – Use shell built-in time while checking exit status of a function and suppressing output

bashtime

I am attempting to do the following in a bash script and am wondering if it is possible:

  • Time the output of a command (in this case, mysql) using time
  • Suppress the stdout output of the command
  • Capture the stderr output of the command in case of an error
  • Check the exit status code of the command after running it

This is a complex chain and I haven't had much luck getting it all to work together. I can get the status code to work if I cease using time.

This is what I had so far:

# the setup
TIMEFORMAT=%R
MYSQL=mysql <options>

# execute and time a query (does more than is listed here)
function executeQuery {

  TIME=$( time ( $MYSQL "$1" 2>&1 | tee /tmp/somefile.txt > /dev/null ) 2>&1 )

  # do something with $?

}

I am redirecting any error response from the command to a file using tee, and then sending the resulting stdout to /dev/null. I am then redirecting the time command's stderr to stdout, which should end up in $TIME.

Now if I change that line to something like:

  TIME=$( time ( $MYSQL "$1" 2>&1 | tee /tmp/somefile.txt > /dev/null; exit ${PIPESTATUS[0]} ) 2>&1 )

It correctly sends back the exit code from mysql, but breaks the time command.

Is this even possible? Have I missed something? Hopefully the goal is clear.

Thanks!

Best Answer

bash time is a major PITA. Its output isn't redirectable without nasty shell hacks like your multiple levels of subshells. http://mywiki.wooledge.org/BashFAQ/032 suggests that the correct answer is:

TIME = $( ( time $MYSQL "$1" 2>&1 | tee /tmp/somefile.txt > /dev/null; exit ${PIPESTATUS[0]} ) 2>&1 )

Note that bash time takes an entire pipeline as an argument, so placing the subshell after the time invocation is incorrect.

This was tested with

TIME = $( ( time ls /asdfasfdsfs 2>&1 | tee asdf 2>&1 >/dev/null ; exit ${PIPESTATUS[0]} ) 2>&1 );
echo $?;
echo $TIME

Which gave me

2
real 0m0.003s user 0m0.004s sys 0m0.004s