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

Re: SOLVED save time output to a script variable (was RE: unexpected script output)



Nelson Green wrote:
> Well, that didn't take long. I'm posting a simple script that captures the
> system time output of the time command to a variable in case anyone else
> has spent a good bit of time trying to get this to work, like me.

I have a few comments.  Many eyes make all bugs visibile! :-)
I am parsnickety but am trying to be helpful.

> This works even if /bin/sh is linked to /bin/dash, so my assumption
> appears to have been correct.

But $RANDOM in this script is a ksh/bash/zsh specific feature.  It
should therefore say one of #!/bin/bash or other to designate it.

> $cat gettime.sh 
> #!/bin/sh

Ahem, #!/bin/bash :-)

> BLOCKSIZE=10000
> INFILE="/dev/urandom"
> OUTFILE="/tmp/random.$RANDOM"

There are many ways to produce $RANDOM like behavior in a standard
portable way.  I like using awk.  But of course on free(dom) software
systems there are two.  GNU awk and mawk.  Something that works
everywhere that I like is this.  But again there are many
possibilities.

  $ awk -v s=$$$(date +%M%S) 'BEGIN{srand(s);print(int(32767*rand()));}'

Use this with $(...) in a script like:

  randval=$(awk -v s=$$$(date +%M%S) 'BEGIN{srand(s);print(int(32767*rand()));}')

Adjust the value 32767 as appropriate.  I will say more about OUTFILE
in a moment.  Because of course when you switch to 'mktemp' you don't
need $RANDOM anymore and this entire thing disappears.

> TIME_CMD="/usr/bin/time"

The TIME_CMD is defined but never used.  But using hard coded paths
are fragile.  Especially when time lives in /bin/time on another
system.  ('basename' is more problematic since it is an example I know
lives in different places on different systems.  'grep' always used to
be trouble.)

> TIME_FORMAT="%S"
> 
> ##################################################
> # Exit status codes:                             #
> ##################################################
> E_BAD_CLEANUP=65

Why 65?  Any non-zero exit is fine of course.

> SUCCESS=0
> 
> ##################################################
> # Script Variables:                              #
> ##################################################
> blockcount=1000
> 
> totaltime=$(/usr/bin/time -f $TIME_FORMAT dd if=$INFILE of=$OUTFILE bs=$BLOCKSIZE \
>    count=$blockcount status=noxfer 2>&1 | sed -e "1,2d")

Instead of using the hard coded path /usr/bin/time please simply quote
the command so that it avoids using a builtin or alias.  Observe the
difference like this:

  $ time sleep 0.1
  real    0m0.102s
  user    0m0.000s
  sys     0m0.000s

  $ \time sleep 0.1
  0.00user 0.00system 0:00.10elapsed 0%CPU (0avgtext+0avgdata 764maxresident)k 0inputs+0outputs (0major+243minor)pagefaults 0swaps

So in your script:
  
  totaltime=$(\time -f $TIME_FORMAT dd if=$INFILE of=$OUTFILE bs=$BLOCKSIZE \
     count=$blockcount status=noxfer 2>&1 | sed -e "1,2d")

(Note: Any quoting is fine.  I find \time more readable than 'time' or
"time" but all three work the same.)

> OUTFILE="/tmp/random.$RANDOM"
  
The $OUTFILE is trouble.  Don't create temporary files this way.
Create temporary files using 'mktemp'.  Do it like this (or any of the
variations, this is the shortest invocation):

  OUTFILE=$(mktemp) || exit 1

The mktemp will Do The Right Thing in all cases.  Easy to use and
trouble free.

  totaltime=$(\time -f $TIME_FORMAT dd if=$INFILE of=$OUTFILE bs=$BLOCKSIZE \
     count=$blockcount status=noxfer 2>&1 | sed -e "1,2d")

If you are using Sid (or in the future Jessie) and coreutils 8.20 or
later then you can use the new GNU dd status=none option and avoid the
sed to delete the status output.  Unfortunately neither Squeeze or
Wheezy have this option yet.  It is very recent in Sid.

  totaltime=$(\time -f $TIME_FORMAT dd if=$INFILE of=$OUTFILE bs=$BLOCKSIZE \
     count=$blockcount status=none)

> echo "Total time: $totaltime"
> 
> # CLEAN-UP:
> if [ -e $OUTFILE ]
> then
>    rm -f $OUTFILE || exit $E_BAD_CLEANUP
> fi
> exit $SUCCESS

Cleanup is always trouble.  What if the user presses Control-C during
the run?  The cleanup will not be run and it will leave trash files
behind.  A trap handler is needed to clean those up.

Here is a snippet of code to create a temporary file and to clean it
up even if interrupted.

  #!/bin/sh
  unset tmpfile
  cleanup() {
    test -n "$tmpfile" && rm -f "$tmpfile"
  }
  trap "cleanup" EXIT
  # Begin dash specific trap handling.
  trap "cleanup; trap - HUP; kill -HUP $$" HUP
  trap "cleanup; trap - INT; kill -INT $$" INT
  trap "cleanup; trap - QUIT; kill -QUIT $$" QUIT
  trap "cleanup; trap - TERM; kill -TERM $$" TERM
  trap "trap - PIPE; cleanup; kill -PIPE $$" PIPE
  # End dash specific trap handling.
  tmpfile=$(mktemp) || exit 1

Then always let the cleanup happen in the cleanup handler.  That way
the cleanup code is always run and tested and it is the same for both
normal cases and interrupted cases.  And of course there are many
variations depending upon specific needs.  But this should give you
good ideas.

Darn that dash for not behaving like ksh/bash/zsh and others in trap
handling!  The others all call the exit trap upon a signal but dash
does not and so needs all signals listed explicitly.  Not having that
section used to be portable before dash came along.  Sigh.

Of course I am using advanced features like functions and named
signals names and $() and calling mktemp without arguments.  :-)
I think it makes it more readable that way and covers the modern
systems.  Making this a little less readable this could be expanded
into the legacy Bourne shell compatible syntax if Solaris systems are
supported.

Bob

Attachment: signature.asc
Description: Digital signature


Reply to: