Search code examples
perldebuggingshellshellexecutesystem-calls

Can I capture shell invocations from Perl?


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.


Solution

  • 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
    ...