3

I have a Perl script which invokes other programs, i.e. it calls system and/or exec and/or open with a pipe and/or uses the backtick operator.

Can I run this script in such a way that it will print out the arguments to each of the above so that I can see what it is calling into?

For example a program like this which I cannot modify

#!/usr/bin/perl
sub get_arg {return "argument$_[0]";}
system "./foo", get_arg(1), get_arg(2);
print `./foo abc def`;

Invoked something perhaps like this

perl --shell-trace-on ./myscript.pl

Which will in this case output

./foo argument1 argument2
./foo abc def

It is acceptable to throw away myscript.pl's normal output or blend it with this trace.

Thanks a lot.

spraff
  • 32,570
  • 22
  • 121
  • 229

2 Answers2

5

This is considered advanced Perl, but you can define subroutines in the CORE::GLOBAL namespace at compile time and hijack Perl's builtin functions. Calling functions in the CORE namespace will invoke the original builtin functions.

BEGIN {
    # have to use a BEGIN block so these functions are defined before
    # run time
    *CORE::GLOBAL::system = sub {
        print STDERR "about to invoke system @_\n";
        return CORE::system(@_);
    };
    *CORE::GLOBAL::qx = sub {
        print STDERR "about to invoke qx/backticks @_\n";
        return CORE::qx(@_);
    };
    *CORE::GLOBAL::exec = sub { ... };
};
system("sleep 5");
print `ls`;
1;

To apply this feature to an arbitrary standalone script, you can put this code into a simple module (say, ShellTrace.pm), and then invoke perl with the -MShellTrace switch. (HT: perlman):

package ShellTrace;
BEGIN {
    *CORE::GLOBAL::system = sub { ... };
    *CORE::GLOBAL::qx = sub { ... };
    *CORE::GLOBAL::exec = sub { ... };
    *CORE::GLOBAL::open = sub { ... };
}
1;

$ perl -MShellTrace ./myscript.pl
about to invoke system ./foo argument1 argument2 
about to invoke qx/backticks ./foo abc def
...
mob
  • 117,087
  • 18
  • 149
  • 283
  • Impressive trick, but it requires modifying the script. Is there a way to invoke the script unchanged but with the environment thus established? – spraff Aug 02 '11 at 15:53
  • 1
    Yes - put those changes in a `.pm` file (e.g. `ShellTrace.pm`, and invoke your script with `perl -MShellTrace ./myscript.pl` – perlman Aug 02 '11 at 16:00
1

No, the system command does not put its executed command in any special variables.

#!/usr/bin/perl
sub get_arg {return "argument$_[0]";}
my $command = './foo ' . join(' ', get_arg(1), get_arg(2));
print "$command\n";
my $resp = `$command`; # or system($command);
print `ls *bar*`;
Victor Bruno
  • 1,033
  • 7
  • 12
  • Edited to clarify -- the script is immutable. I'm looking for a solution that's analogous to `strace`. – spraff Aug 02 '11 at 14:46