74

I want to run time command to measure time of several commands.

What I want to do is:

  • Measure the time of running of all of them added together
  • Write the time output to a file
  • Write the STDERR from the command I am measuring to STDERR

What I do NOT want to do is

  • Write the several commands into a separate script (why? because all of this is already a script that I am generating programatically, and creating ANOTHER temporary script would be more mess than I want)

What I tried so far:

/usr/bin/time --output=outtime -p echo "a"; echo "b";

Doesn't work, time is run only on the first one.

/usr/bin/time --output=outtime -p ( echo "a"; echo "b"; )

Doesn't work, ( is unexpected token.

/usr/bin/time --output=outtime -p { echo "a"; echo "b"; }

Doesn't work, "no such file or directory".

/usr/bin/time --output=outtime -p ' echo "a"; echo "b";'

Doesn't work, "no such file or directory".

time ( echo "a"; echo "b"; ) 2>outtime

Doesn't work, since it redirects all STDERR into outtime; I want only the time output there.

And of course,

time --output=outime echo "a";

Doesn't work, since --output=outime: command not found.

How can I do it?

100

Use sh -c 'commands' as the command, e.g.:

/usr/bin/time --output=outtime -p sh -c 'echo "a"; echo "b"'
| improve this answer | |
  • 2
    a shorter version: time -p sh -c 'echo "a"; echo "b"' – Geo Jun 2 '19 at 2:08
  • Is it expected that aliases won't work in the sequence of commands? – Muffo Aug 10 at 20:40
  • Although this answer works great, I prefer a version shared at superuser.com/a/608596/135762 because it makes the code much readable, instead of just being passed as a string to sh -c. Anyway, Can somebody please point a situation when one would be preferred over another? – Bhavesh Diwan Aug 13 at 1:20
  • The version shared there runs multiple commands, but does not save the time output to a file, as this question requested. If you read this question closely, you'll see that using parenthesis to make a subshell was mentioned as not a solution. – Jim Paris Aug 14 at 19:32
  • 1
    @muffo You are spawning a new shell, so aliases will only work if the new shell you're spawning defines them too. You might find something like bash -i -c 'echo foo' to work better than sh -c 'echo foo' in that regard. – Jim Paris Aug 14 at 19:34
16

Not the correct answer but very related to the question.
Get timing statistics for multiple programs combined parentheses are required. Separate commands with semicolons ; or &&, if command2 should only be run, when command1 exited without error:

time ( command1 ; command2 )

time ( command1 && command2 )
| improve this answer | |
  • 3
    This is nice. Better yet, use && between the commands -- like so time ( command1 && command2 ) so that if the first command fails; it won't proceed to executing the other. – bikashg May 8 '19 at 11:03
  • 2
    @bikashg It very much depends on what you want to run. In my case I use | because I'm piping between three commands. – Alexis Wilke Apr 13 at 18:53
9

Try this:

% (time ( { echas z; echo 2 } 2>&3 ) ) 3>&2 2>timeoutput
zsh: command not found: echas
2
% cat timeoutput                                
( { echas z; echo 2; } 2>&3; )  0.00s user 0.00s system 0% cpu 0.004 total

Explanation:

First, we have to find a way to redirect the output of time. Since time is a shell builtin, it takes the full command line as the command to be measured, including redirections. Thus,

% time whatever 2>timeoutput
whatever 2> timeoutput  0.00s user 0.00s system 0% cpu 0.018 total
% cat timeoutput 
zsh: command not found: whatever

[Note: janos's comment implies this is not the case for bash.] We can achieve the redirection of time's output by running time in a subshell and then redirecting the output of that subshell.

% (time whatever) 2> timeoutput
% cat timeoutput 
zsh: command not found: whatever
whatever  0.00s user 0.00s system 0% cpu 0.018 total

Now we have successfully redirected the output of time, but its output is mixed with the error output of the command we are measuring. To separate the two, we use an additional file descriptor.

On the "outside" we have

% (time ... ) 3>&2 2>timeout

This means: whatever is written to file descriptor 3, will be output to the same place file descriptor 2 (standard error) is outputting now (the terminal). And then we redirect standard error to the file timeout.

So now we have: everything written to stdout and fd 3 will go to the terminal, and everything written to stderr will go to the file. What's left is to redirect the measured command's stderr to fd 3.

% (time whatever 2>&3) 3>&2 2>timeout

Now, to make time measure more than one command, we need to run them in an(other!) subshell (inside parentheses). And to redirect the error output of all of them to fd 3, we need to group them inside curly brackets.

So, finally, we arrive at:

% (time ( { whatever; ls } 2>&3 ) ) 3>&2 2>timeoutput

That's it.

| improve this answer | |
  • This is a syntax error in a POSIX shell. Probably a bashism? – josch Aug 1 '17 at 6:00
  • @josch the shell used here is zsh. – angus Aug 28 '17 at 13:22

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.