Size: 2728
Comment:
|
Size: 2795
Comment: Adjust comments for phone displays
|
Deletions are marked like this. | Additions are marked like this. |
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. |
# Explicit, but inefficient. bash -c "time ls" 2>time.output # Slightly more efficient. ( time ls ) 2>time.output # Most efficient. { time ls; } 2>time.output |
Line 24: | Line 26: |
foo=$( bash -c "time ls" 2>&1 ) # Captures *everything*. foo=$( { time ls; } 2>&1 ) # More efficient version. |
# Captures *everything*. foo=$( bash -c "time ls" 2>&1 ) # More efficient version. foo=$( { time ls; } 2>&1 ) |
Line 28: | Line 31: |
# 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. |
# The shell's original FD 1 is saved in FD 3, which is inherited by the subshell. # Inside the innermost block, we send the time command's stdout to FD 3. |
Line 31: | Line 34: |
foo=$( { time bar 1>&3; } 2>&1 ) # Captures stderr and time. | # Captures stderr and time. foo=$( { time bar 1>&3; } 2>&1 ) |
Line 42: | Line 46: |
See FileDescriptor for full explanations of the redirection juggling. | |
Line 59: | Line 64: |
./coredump >log 2>&1 # Fails to capture the message { ./coredump; } >log 2>&1 # Captures the message |
# Fails to capture the message ./coredump >log 2>&1 # Captures the message { ./coredump; } >log 2>&1 |
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:
# Explicit, but inefficient. bash -c "time ls" 2>time.output # Slightly more efficient. ( time ls ) 2>time.output # Most efficient. { time ls; } 2>time.output # The general case: { time some command >stdout 2>stderr; } 2>time.output
# Captures *everything*. foo=$( bash -c "time ls" 2>&1 ) # More efficient version. foo=$( { time ls; } 2>&1 ) # Keep stdout unmolested. # The shell's original FD 1 is saved in FD 3, which is inherited by the subshell. # Inside the innermost block, we send the time command's stdout to FD 3. exec 3>&1 # Captures stderr and time. foo=$( { time bar 1>&3; } 2>&1 ) 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
See FileDescriptor for full explanations of the redirection juggling.
- 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:
# Fails to capture the message ./coredump >log 2>&1 # Captures the message { ./coredump; } >log 2>&1
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.