Differences between revisions 1 and 2
Revision 1 as of 2007-05-02 23:20:44
Size: 673
Editor: redondos
Comment:
Revision 2 as of 2007-06-14 20:51:30
Size: 1496
Editor: GreyCat
Comment: expand. and correct. my god, correct that wrong stuff!
Deletions are marked like this. Additions are marked like this.
Line 3: Line 3:
The reason that 'time' needs special care for redirecting its output is one of those mysteries of the universe. The answer will probably be solved around the same time we find dark matter. 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 different shell or block, and redirect stderr of ''that'' shell 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 inner shell/block. For example:
Line 7: Line 15:
     bash -c "time ls" > /path/to/foo 2>&1
     ( time ls ) > /path/to/foo 2>&1
     { time ls; } > /path/to/foo 2>&1
   bash -c "time ls" 2>time.output
   ( time ls ) 2>time.output
   { time ls; } 2>time.output
   # The general case:
   { time some command >stdout 2>stderr; } 2>time.output
Line 14: Line 24:
     foo=$( bash -c "time ls" 2>&1 )
     foo=$( ( time ls ) 2>&1 )
     foo=$( { time ls; } 2>&1 )
   foo=$( bash -c "time ls" 2>&1 ) # Captures *everything*.

   # Keep stdout unmolested.
   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 2>&4; } 2>&1 1>&3) # Captures time only.
   exec 3>&- 4>&-
Line 18: Line 36:

Note: Using 'bash -c' and ( ) creates a subshell, using { } does not. Do with that as you wish.

Anchor(faq32)

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 different shell or block, and redirect stderr of that shell 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 inner shell/block. For example:

  • File Redirection

   bash -c "time ls" 2>time.output
   ( time ls ) 2>time.output
   { time ls; } 2>time.output
   # The general case:
   { time some command >stdout 2>stderr; } 2>time.output
  • Variable Redirection

   foo=$( bash -c "time ls" 2>&1 )       # Captures *everything*.

   # Keep stdout unmolested.
   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 2>&4; } 2>&1 1>&3)  # Captures time only.
   exec 3>&- 4>&-

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