I have a perl script that calls external executables using system()
. I would like to measure the CPU seconds taken by these external programs. Ideally, I would like to run them using the shell builtin time
command (this is on a Linux system). Something like this:
system("time /path/to/command")
Now, time
prints its output to stderr, but in order to do so, launches the command it is given in a separate subshell. This means that in order to capture time's output when running manually in the shell, you need to explicitly use a subshell and redirect the subshell's stderr:
$ time ( command > command.log 2> command.er) 2> time.out
The file time.out
will have the output of the time
command while command.er
has the stderr of command
. Unfortunately, the parentheses break perl's system call:
$ time ( ls ) 2> er ## works
$ perl -e 'system("time (ls)")'
sh: 1: Syntax error: word unexpected (expecting ")")
And this means I can't capture the output of time
. To make matters wors, this seems to be version dependent:
$ perl --version | head -n2
This is perl 5, version 18, subversion 2 (v5.18.2) built for x86_64-linux-gnu-thread-multi
But if I try the same thing with a newer version:
$ perl --version | head -n2
This is perl 5, version 24, subversion 1 (v5.24.1) built for x86_64-linux-thread-multi
$ perl -e 'system("time (ls)")'
file1
real 0m0.002s
user 0m0.000s
sys 0m0.000s
Unfortunately, I need this to run on a production machine so upgrading Perl is not an option. So, how can I time a system call in Perl 5.18? I need the user
and sys
values, so simply recording the start and end times won't help. I am willing to use a dedicated module if that's necessary although I would prefer a trick that lets me use the shell's time
.
UPDATE: it turns out the difference in behavior is not because of the newer perl version but, instead, it is because I tested it on an Arch system whose /bin/sh
is bash
while the other commands were being run on Ubuntu systems whose /bin/sh
is dash
, a minimal shell that doesn't support parentheses for subshells.
You've tested the command with
bash
, but you passed it tosh
.is short for
but you want
The problem is that in first case, your shell is probably
/bin/bash
whereas in second case it is/bin/sh
. If you want to run your command with another shell, you could usesystem LIST
form:Note 1: There's
PERL5SHELL
environmnet value, but that seems to take effect only on Win32.Note 2: If you want to measure CPU time of child process, you could use Unix::Getrusage or BSD::Resource modules.
You can use Capture::Tiny to capture the STDOUT and STDERR of pretty much anything in Perl.
For some reason the output is missing some whitespace on my system, but is clear enough to parse out what you need.