bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: feature: time builtin and file descriptor


From: Eduardo A . Bustamante López
Subject: Re: feature: time builtin and file descriptor
Date: Fri, 31 Oct 2014 09:27:20 -0700
User-agent: Mutt/1.5.21 (2010-09-15)

> OK.  It doesn't sound like this feature is of general interest.  Since
> you can control when you open and close file descriptors, you might look
> at $SECONDS when the file is opened and when it's closed and using the
> difference to see how long it was open.

If I understand correctly, the original issue wasn't exactly to
measure the time FDs were open, but to use that measure to time the
execute of code blocks. That is, timing open FDs was just a tool, not
the goal itself. Since we can already time blocks, I think this
alternative is not needed. I wrote a detailed explanation here:


address@hidden:~$ bash ./script
processing $i=1
item $i=1 took 4.003
processing $i=2
item $i=2 took 6.003
processing $i=3
item $i=3 took 8.003
all together took 18.014

address@hidden:~$ cat ./script
#!/bin/bash

# set TIMEFORMAT to show only the 'elapsed time in seconds'. (see man bash)
TIMEFORMAT=%R

# The key points are:
# 1. You can 'time' a block, so, stuff like: time { ...; } and time for ...; do 
...
#    are valid. You don't need to trace an open file descriptor to time a block
#    of commands.
# 2. Capturing the output of 'time' is tricky, but it can be done, follow the
#    indications here: http://mywiki.wooledge.org/BashFAQ/032 (or read my
#    attempt at explaining it at the end)

{ g=$(
    { time for i in 1 2 3; do
        echo "processing \$i=$i"

        { t=$(
          { time {
              # item commands
              sleep "$i"
              sleep "$((i+2))"
            }
          } 2>&1 >&3
        );} 3>&1

        echo "item \$i=$i took $t"
      done
    } 2>&1 >&3
);} 3>&1

echo "all together took $g"


# How to do what I think you want to do:
#
# 1. Change TIMEFORMAT to output just the number you want (easy)
# 2. Now, the tricky part, capture the output of time:
#    - You can't just do: var=$(time ...) and hold the output of time in var,
#      because 'time' writes to stderr. So the logical thing follows.
#    - We try: var=$(time ... 2>&1 >/dev/null) (we discard stdout for
#      simplicity), but that still doesn't work, because that redirection
#      doesn't seem to apply to 'time', instead, it applies to the command
#      we're timing.
#    - So, we try: var=$({ time ...; } 2>&1 >/dev/null), aaaaand... that works.
#      By doing the redirection on a group that consists only of 'time', we
#      managed to capture its output.
#
#      You can try this at home:
#
# $ TIMEFORMAT=%R; var=$({ time sleep 3.1416;} 2>&1 >/dev/null); echo "$var"
# 3.143
#
# 3. So, we have the elapsed time... but, what if we also want to keep stdout?
#    We have to do some tricky redirections:
#   
#    { var=$({ time ...; } 2>&1 >&3); } 3>&1
#
#    Don't panic, it looks horrible, but it's actually easy to understand. The
#    first thing we should know is that redirections apply following these two
#    rules:
#
#    a. If you have nested blocks, it will apply the outer redirections first.
#       So, in { { { foo >a; } >b; } >c; } >d
#       It will first redirect to 'd', then to 'c', then to 'b', ...
#    b. Redirections done at the same level are executed from left to right.
#
#    With these two details we can proceed to understand:
#
#    { var=$({ time ...; } 2>&1 >&3); } 3>&1
#
#    Since we execute the outer redirections first, '3>&1' is the first
#    redirection, so now there's a file descriptor 3, open for the whole block
#    delimited by { ... }. Ah, and that FD3 is a clone of the script's stdout,
#    which could be a file, the terminal, etc. Then, inside var=$({...} redirs)
#    we have another set of redirections, '2>&1' and '>&3'. They get executed
#    from left to right, so '2>&1' goes first. What this does is to send stderr
#    (time's output), to stdout *but* in this case, stdout goes to $(...),
#    which is captured in 'var' (it doesn't go to the main script stdout). And
#    then we have '>&3', which does: send stdout (which currently was going to
#    $(...) ) to the main script's stdout (&3 is a clone of it).
#
#    Sorry for the complex explanation.
#
#    And, if I read your original email correctly, this is what you wanted to 
do:
#
#      > This idea came to my mind while writing a script that runs multiple
#      > commands, and I simply wanted to know how long they are busy.  I am
#
#    So, now you can run 'time' multiple times, even nested, and find out how
#    long they are busy, without having to patch bash. (Sure, the code is ugly, 
#    but, it's bash, it's expected to be like that ;) ).
#
#      > What do you think, useful feature or unnecessary bloat?
#   My opinion is: unnecessary bloat, since we can work-around that specific
#   case without new features. Sure, the work-around is ugly, but, it's
#   consistent, and, with a bit of effort, it can do almost everything your
#   proposal attempts to achieve.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]