Differences between revisions 12 and 14 (spanning 2 versions)
Revision 12 as of 2012-12-14 09:02:56
Size: 2426
Editor: geirha
Comment: A slightly more useful example for pipe
Revision 14 as of 2018-07-26 18:25:39
Size: 2728
Editor: xdsl-213-168-89-171
Comment:
Deletions are marked like this. Additions are marked like this.
Line 6: Line 6:
   time find ... | xargs ... time find ... | xargs ...
Line 14: Line 14:
   bash -c "time ls" 2>time.output # Explicit, but inefficient.
   ( time ls ) 2>time.output # Slightly more efficient.
   { time ls; } 2>time.output # Most efficient.
bash -c "time ls" 2>time.output # Explicit, but inefficient.
( time ls ) 2>time.output # Slightly more efficient.
{ time ls; } 2>time.output # Most efficient.
Line 18: Line 18:
   # The general case:
   { time some command >stdout 2>stderr; } 2>time.output
# The general case:
{ time some command >stdout 2>stderr; } 2>time.output
Line 24: Line 24:
   foo=$( bash -c "time ls" 2>&1 ) # Captures *everything*.
   foo=$( { time ls; } 2>&1 ) # More efficient version.
foo=$( bash -c "time ls" 2>&1 ) # Captures *everything*.
foo=$( { time ls; } 2>&1 ) # More efficient version.
Line 27: Line 27:
   # Keep stdout unmolested.
   exec 3>&1
   foo=$( { time bar 1>&3; } 2>&1 ) # Captures stderr and time.
   exec 3>&-
# Keep stdout unmolested.
# This works because fd 1 and fd 2 change in a subshell but fd 3 stays the same.
# Therefore fd 1 of the subshell is redirected to the original shell's fd 1.
exec 3>&1
foo=$( { time bar 1>&3; } 2>&1 ) # Captures stderr and time.
exec 3>&-
Line 32: Line 34:
   # Keep both stdout and stderr unmolested.
   exec 3>&1 4>&2
   foo=$( { time bar 1>&3 2>&4; } 2>&1 ) # Captures time only.
   exec 3>&- 4>&-
# Keep both stdout and stderr unmolested.
exec 3>&1 4>&2
foo=$( { time bar 1>&3 2>&4; } 2>&1 ) # Captures time only.
exec 3>&- 4>&-
Line 37: Line 39:
   # same thing without exec
   { foo=$( { time bar 1>&3- 2>&4-; } 2>&1 ); } 3>&1 4>&2
# same thing without exec
{ foo=$( { time bar 1>&3- 2>&4-; } 2>&1 ); } 3>&1 4>&2
Line 43: Line 45:
    # Make time only output elapsed time in seconds
    TIMEFORMAT=%R
    # Keep stdout and stderr unmolested
    exec 3>&1 4>&2
    { time foo 1>&3 2>&4; } 2>&1 | awk '{
     printf "The task took %d hours, %d minutes and %.3f seconds\n",
               $1/3600, $1%3600/60, $1%60
    }'
    exec 3>&- 4>&-
# Make time only output elapsed time in seconds
TIMEFORMAT=%R
# Keep stdout and stderr unmolested
exec 3>&1 4>&2
{ time foo 1>&3 2>&4; } 2>&1 | awk '{
    printf "The task took %d hours, %d minutes and %.3f seconds\n",
           $1/3600, $1%3600/60, $1%60
}'
exec 3>&- 4>&-
Line 57: Line 59:
   ./coredump >log 2>&1 # Fails to capture the message
   { ./coredump; } >log 2>&1 # Captures the message
./coredump >log 2>&1 # Fails to capture the message
{ ./coredump; } >log 2>&1 # Captures the message
Line 60: Line 62:

The same applies to job control messages:

{{{
$ { sleep 1 & } >log 2>&1
$ cat log
[1] 10316
[1]+ Done sleep 1
}}}

Of course you may opt to redirect to `/dev/null` instead of a file.
Line 61: Line 75:
CategoryShell CategoryShell CategoryExampleCode

How can I redirect the output of 'time' to a variable or file?

Bash's time keyword uses special trickery, so that you can do things like

time find ... | xargs ...

and get the execution time of the entire pipeline, rather than just the simple command at the start of the pipe. (This is different from the behavior of the external command time(1), for obvious reasons.)

Because of this, people who want to redirect time's output often encounter difficulty figuring out where all the file descriptors are going. It's not as hard as most people think, though -- the trick is to call time in a SubShell or block, and then capture stderr of the subshell or block (which will contain time's results). If you need to redirect the actual command's stdout or stderr, you do that inside the subshell/block. For example:

  • File redirection:
    bash -c "time ls" 2>time.output      # Explicit, but inefficient.
    ( time ls ) 2>time.output            # Slightly more efficient.
    { time ls; } 2>time.output           # Most efficient.
    
    # The general case:
    { time some command >stdout 2>stderr; } 2>time.output
  • CommandSubstitution:

    foo=$( bash -c "time ls" 2>&1 )       # Captures *everything*.
    foo=$( { time ls; } 2>&1 )            # More efficient version.
    
    # Keep stdout unmolested.
    # This works because fd 1 and fd 2 change in a subshell but fd 3 stays the same.
    # Therefore fd 1 of the subshell is redirected to the original shell's fd 1.
    exec 3>&1
    foo=$( { time bar 1>&3; } 2>&1 )      # Captures stderr and time.
    exec 3>&-
    
    # Keep both stdout and stderr unmolested.
    exec 3>&1 4>&2
    foo=$( { time bar 1>&3 2>&4; } 2>&1 )  # Captures time only.
    exec 3>&- 4>&-
    
    # same thing without exec
    { foo=$( { time bar 1>&3- 2>&4-; } 2>&1 ); } 3>&1 4>&2
  • Pipe:
    # Make time only output elapsed time in seconds
    TIMEFORMAT=%R
    # Keep stdout and stderr unmolested
    exec 3>&1 4>&2
    { time foo 1>&3 2>&4; } 2>&1 | awk '{
        printf "The task took %d hours, %d minutes and %.3f seconds\n",
               $1/3600, $1%3600/60, $1%60
    }'
    exec 3>&- 4>&-

A similar construct can be used to capture "core dump" messages, which are actually printed by the shell that launched a program, not by the program that just dumped core:

./coredump >log 2>&1           # Fails to capture the message
{ ./coredump; } >log 2>&1      # Captures the message

The same applies to job control messages:

$ { sleep 1 & } >log 2>&1
$ cat log
[1] 10316
[1]+  Done                    sleep 1

Of course you may opt to redirect to /dev/null instead of a file.


CategoryShell CategoryExampleCode

BashFAQ/032 (last edited 2018-07-26 21:28:38 by GreyCat)