0

I am attempting to write a function which will take two commands as inputs, time the executions of both of them, then output those times to a text file. After reading this post, I got most of the way there, and can write my execution times to a file one at a time. The problem I have now is getting the function to accept two multi-word inputs. My current code looks like:

timeDiff () {
  { time "$1" ; } 2> ~/file.txt
  { time "$2" ; } 2>> ~/file.txt
}

If I run these lines sequentially with the functions I want, everything is fine. The file is overwritten with the time info of the two functions. Here are some of the attempts and problems I have with this:

timeDiff grep "str" file1 query database lookup.sql

This will cause my file to have a warning on grep, some times, and a bash: str: command not found.

timeDiff 'grep "str" file1' 'query database lookup.sql'

This will cause my file to have bash:grep "str" file1: No such file or directory, some times, and bash:query database lookup.sql: No such file or directory.

I think that this is related to how I need quotation marks for my grep, but perhaps there's a better way of writing the inputs for the functions. I'm a beginner, so I'm eager to learn! Thanks!

2 Answers2

1

With:

 { time "$1" ; } 2> ~/file.txt

With a shell such as bash that has time as a keyword, you're using the time keyword to time the evaluation of the "$1" shell code. "$1" is code that executes the command whose name is in the first position parameter to the function:

With:

timeDiff grep "str" file1 query database lookup.sql

You're invoking timeDiff with grep as first argument and str as second argument (and file1 as third, etc.)

So time "$1" will time the execution of the command called grep without argument. grep requires at least one argument so will complain.

In

timeDiff 'grep "str" file1' 'query database lookup.sql'

This time $1 will be grep "str" file1 but it's very unlikely there's a command by that name.

What it looks like you want is to time the evaluation of shell code passed in the first and second arguments like:

timeDiff () {
  time eval " $1"
  time eval " $2"
} 2> ~/file.txt

Or possibly:

timeDiff () {
  eval "time {
    $1
  }"
  eval "time {
    $2
  }
} 2> ~/file.txt

To evaluate shell code consisting of:

time {
  contents-of-$1
}

To time the command group whose body is the contents of the first position argument.

Also beware that in:

{ time cmd; } 2> file

The timing will end-up in file, but also the errors of cmd (and the errors of the shell failing to run cmd if any).

To get only the timing in file, you'd need something like:

{ time cmd 2>&3 3>&-; } 3>&2 2> file
0
timeDiff grep "str" file1 query database lookup.sql

will make $1=grep and $2=str

timeDiff 'grep "str" file1' 'query database lookup.sql'

will make $1 the string "grep "str" file1" so it looks for a file called

./grep_"str"_file1

Where the _ are space characters: there is no such file.

You need to use bash's eval function to turn a string into an executable command line statement.

But be careful they say "eval is evil" for a reason :)

teknopaul
  • 703
  • It looks for a file called grep "str" file1 in $PATH, not in the current working directory (unless $PATH contains a . or empty string element which would be a bad idea). – Stéphane Chazelas Oct 03 '22 at 19:07