In Perl, you can invoke external programs in many different ways. You can capture the output of an external program using backticks:
$date = `/bin/date`;
You can open up a pipe to a program:
open (SORT, " | /usr/bin/sort | /usr/bin/uniq");
You can invoke an external program and wait for it to return with system():
system "/usr/bin/sort < foo.in";
or you can invoke an external program and never return with exec():
exec "/usr/bin/sort < foo.in";
All of these constructions can be risky if they involve user input that may contain shell metacharacters. For system() and exec(), there's a somewhat obscure syntactical feature that allows you to call external programs directly rather than going through a shell. If you pass the arguments to the external program, not in one long string, but as separate members in a list, then Perl will not go through the shell and shell metacharacters will have no unwanted side effects. For example:
system "/usr/bin/sort","foo.in";
You can take advantage of this feature to open up a pipe without going through a shell. By calling open on the magic character sequence |-, you fork a copy of Perl and open a pipe to the copy. The child copy can then exec another program using the argument list variant of exec().
my $result = open (SORT,"|-"); die "Couldn't open pipe to subprocess" unless defined($result); exec "/usr/bin/sort",$uservariable or die "Couldn't exec sort" if $result == 0; for my $line (@lines) { print SORT $line,"\n"; } close SORT;
The initial call to open() tries to fork a copy of Perl. If the call fails it returns an undefined value and the scrypt immediately dies (you might want to do something more sophisticated, such as sending an HTML error message to the user). Otherwise, the result will return zero to the child process, and the child's process ID to the parent. The child process checks the result value, and immediately attempts to exec the sort program. If something fails at this point, the child quits.
The parent process can then print to the SORT filehandle in the normal way.
To read from a pipe without opening up a shell, you can do something similar with the sequence -|:
$result = open(GREP,"-|"); die "Couldn't open pipe to subprocess" unless defined($result); exec "/usr/bin/grep",'-i',$userpattern,$filename or die "Couldn't exec grep" if $result == 0; while (<GREP>) { print "match: $_"; } close GREP;
These are the forms of open() you should use whenever you would otherwise perform a piped open to a command.
An even more obscure feature allows you to call an external program and lie to it about its name. This is useful for calling programs that behave differently depending on the name by which they were invoked.
The syntax is
system $real_name "fake_name","argument1","argument2"
For example:
$shell = "/bin/sh"
system $shell "-sh","-norc"
This invokes the shell using the name "-sh", forcing it to behave interactively. Note that the real name of the program must be stored in a variable, and that there's no comma between the variable holding the real name and the start of the argument list.
There's also a more compact syntax for this construction:
system { "/bin/sh" } "-sh","-norc"
0 comments, (598 reads) All Articles by, GentleGiant