3

I have a bash time keyword command which I cannot fully explain, but it works for me.

My Goal:

  1. to find the execution time of some python script, which takes a variable btw, from within a bash script, and capture that value.

  2. also to capture the output of the python script in the same variable, for success or failure analysis.

After reading around I ended up with this command, but I am not fully able to explain it.

ttime=$( (TIMEFORMAT="%U^"; time  /../myscript.py ${__myvar} 2>&1 )|& tr -d f)

Can you explain why I need the second command tr -d f in the pipe, for the time value to be appended to the output of the command,

The choice of 'tr -d f' was entirely arbitrary, and will not affect my script's output, std or err, but without it, or another command which acts on the scripts text output in some minor way, I see the return from the python without the time appended, why?

Edit:

The real question should have been why is the |& tr -d f . needed, and as Stephanie says it is the |& which is allowing the timings found in the sterr from the time keyword to be passed into the pipeline output

Solution now looks like:

ttime=$(TIMEFORMAT="%U^"; { time /../myscript.py ${__myvar} ; } 2>&1 )

Field Descriptor Tutorial:

http://wiki.bash-hackers.org/howto/redirection_tutorial

  • time is not a Bash builtin. times is, – fpmurphy Nov 2 '17 at 13:17
  • Edited accordingly, txs for the correction @fpmurphy – PuzzleTime Nov 3 '17 at 9:27
4

In bash, like in ksh, time is a keyword (not builtin) that is used to time a pipeline (not only simple command, it can also time compound commands).

In time cmd 2> something, we're timing cmd 2> something and printing the output to stderr, but to the original stderr.

You need stderr redirected before the time construct is invoked. Which you do with your |& that redirects the stdout and stderr of the subshell time is run in, but a much simpler way to do it would be:

time=$(TIMEFORMAT="%U^"; { time cmd; } 2>&1)

That doesn't involve a subshell (here we use a command group instead) nor an extra command.

Note that with bash:

time=$(time (cmd) 2>&1)

happens to work by accident. I wouldn't rely on that as it might change in future versions and doesn't work in other shells that have a time keyword.

If you wanted only the timing output in $time (and not the command's stdout or stderr), you'd do:

{ time=$(TIMEFORMAT="%U^"; { time cmd 2>&3 3>&-; } 2>&1); } 3>&1
  • I do not think you answered the question - why is the time output only appended when the second command is in the pipeline. I tried it without the trcommand and the |& but still did not capture both command output and time output. – PuzzleTime Nov 2 '17 at 13:51
  • @PuzzleTime, as I said (see edit for further clarification), it's the |& that redirects the fd where the timing is written that does the trick in your example. You need to redirect stderr before the time ... construct is run which (time ...) |& ... or { time ...; } 2>&1 do, but not time cmd 2>&1. – Stéphane Chazelas Nov 2 '17 at 16:18
  • I followed up on the use of field descriptors and their duplication, and noted credits to you doted around. I now see what the constructs really are here. Thank you. I am not really happy with the answer where you say 'happens to work by accident' though. Please explain what you mean, a side effect? in computing 'by accident' cannot be the right answer. – PuzzleTime Nov 6 '17 at 14:39
  • @PuzzleTime, it's an accident of implementation, not by design. IIRC there's a Q&A here that covers it. It's been discussed on the bash and/or austin-group-l mailing list as well. – Stéphane Chazelas Nov 6 '17 at 15:11

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.