26

I've been struggling with this problem when writing a bash script. Basically, I want to measure the time of a program on a remote server, so I use the command: /usr/bin/time -f %e sh -c "my command > /dev/null 2>&1" to execute the program. However, it appears that I cannot capture the output of my command (SSH) to a variable at all. In fact, the result (time) keeps getting printed out to stdout.

The full code is:

respond=$(ssh ${fromNode} /usr/bin/time "-f" "%e" "'sh' '-c' 'virsh migrate --live ${VM} qemu+ssh://${toNode}/system --verbose > /dev/null 2>&1'")

The value of respond is just empty, though the time is printed out to the standard output.

Ciro Santilli OurBigBook.com
  • 347,512
  • 102
  • 1,199
  • 985
Andy Dang
  • 1,649
  • 3
  • 14
  • 9

2 Answers2

47

"time" command prints result to stderr, not to stdout. Thus it is not piped into your variable.

You should reroute stderr to stdout to achieve what you want:

 result=$(ssh host time "command" 2>&1)

And your full code can look something like this:

 respond=$(ssh ${fromNode} /usr/bin/time "-f" "%e" "'sh' '-c' 'virsh migrate --live ${VM} qemu+ssh://${toNode}/system > /dev/null 2>&1'" 2>&1)
Rogach
  • 26,050
  • 21
  • 93
  • 172
  • 1
    Great. Works exactly as I wanted it to. Thank you very much for your great help! And by the way, I just simply use &> /dev/null for the virsh migrate part since I intended to discard the output anyway – Andy Dang Aug 21 '12 at 07:57
3

Try swapping the order of your redirections around (to 2>&1 >/dev/null). Your current code is sending both stdout and stderr to /dev/null (so I'm kind of curious as to why anything is printed at all).

Why is this necessary? The syntax 2>&1 means 'duplicate stdout (descriptor 1) as stderr (descriptor 2)'; in effect, stderr is made into a copy of the current stdout. If you put >/dev/null first, then stdout is first redirected to /dev/null, and then stderr is pointed at the current stdout, i.e. /dev/null.

But if you put >/dev/null second, stderr will first become a copy of the current stdout (the normal output stream), before stdout is redirected. So the command's stderr prints to the tty (or the interpreter) as if it came from stdout, while stdout is silenced. This is the behaviour you want.

From man bash:

Note that the order of redirections is significant. For example, the command

ls > dirlist 2>&1

directs both standard output and standard error to the file dirlist, while the command

ls 2>&1 > dirlist

directs only the standard output to file dirlist, because the standard error was duplicated as standard output before the standard output was redirected to dirlist.

nneonneo
  • 171,345
  • 36
  • 312
  • 383
  • Those redirections affect only 'virsh' command (silencing it), not the output from 'time' command. – Rogach Aug 21 '12 at 05:42
  • Though I'm curious, as well, why output of command with 'verbose' flag should be silenced :) – Rogach Aug 21 '12 at 05:42
  • Ah, you are right. I misread the command. Seems my quote-matching skills need improvement :) – nneonneo Aug 21 '12 at 05:44
  • By the way, great explanation - I always wondered about this behavior, your answer explained things. – Rogach Aug 21 '12 at 05:48
  • The verbose option is to force the virsh migrate command to keep the process foreground. It's not really needed, but just in case some virsh version may send the process into background. – Andy Dang Aug 21 '12 at 07:52
  • Thanks for the great explanation about the redirection thing. But I think the problem here was that I need to redirect the output of the time command into the standard output – Andy Dang Aug 21 '12 at 07:56