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 can use Capture::Tiny to capture the STDOUT and STDERR of pretty much anything in Perl.
use Capture::Tiny 'capture';
my ($stdout, $stderr, $exit) = capture { system "time ls" };
print $stderr;
For some reason the output is missing some whitespace on my system, but is clear enough to parse out what you need.
0.00user 0.00system 0:00.00elapsed 0%CPU (0avgtext+0avgdata 2272maxresident)k
0inputs+8outputs (0major+111minor)pagefaults 0swaps
You've tested the command with bash
, but you passed it to sh
.
system("time (ls)")
is short for
system("/bin/sh", "-c", "time (ls)")
but you want
system("/bin/bash", "-c", "time (ls)")
$ time ( ls ) 2> er ## works
$ perl -e 'system("time (ls)")'
sh: 1: Syntax error: word unexpected (expecting ")")
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 use system LIST
form:
system("/bin/bash", "-c", "time(ls)")
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.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.