There are several ways to run an external program in Perl 6, let’s take a look at each way in turn.
shell
Shell is used to pass an expression to the system shell. The shell function invokes the system shell and passes a string argument to it. The return value of `shell` is the exit code of the program:
my $exit_code = shell 'echo "Hello, World!"'; say $exit_code; # 0 == success
If you want redirects, pipes, globs and other shell features to work in your command lines, this is the way to go.
run
Run is used to execute a program. The run function accepts a stringified program name and optional list of arguments to pass to the external program. Like shell, the return value is the exit code of the program:
my $exit_code = run 'echo', 'Hello, World!'; say $exit_code; # 0 == success
Capturing Output
What if you wanted to capture the output of a command, rather than just know if the command was executed successfully or not? Just use a quoting operator “q” and “qq” with the “:x” adverb to execute an expression:
my $message = q:x/echo "Hello, World!"/; say $message; # Hello, World!
If you want to interpolate the expression, use qq:
my $audience = 'Everyone'; my $message = qq:x/echo "Hello, $audience!"/; say $message; # Hello, Everyone!
With quoting mechanisms in Perl 6, the colon is optional for the first adverb so they can be slightly shortened as “qx” and “qqx”.
You’re not limited to slurping results into a single scalar either. Split strings into a list:
# get a list of paths (also see %*ENV) my @env_path = qx/echo $PATH/.split(':'); # Unix-based systems my @env_path = qx/echo %PATH%/.split(';'); # Windows version
Or read a program’s output into an array of lines:
my @commit_results = qqx/git commit -am "$message"/.lines;
Or extract a particular value from tabular data:
# requires "free" program my $free_memory_mb = qx/free -m/.lines[1].words[3]
These examples just scratch the surface of what’s possible, but they should give you enough to get started any time you need to run an external program. Give them a shot!
More Info
- shell and run are described in Synopsis 29
- The execute `x` adverb is described in Synopsis 2
So how can you capture output without using the shell? For instance, if you have special characters in your git commit message and want to avoid the hassle of escaping them for the shell?
In perl5 I aways disliked the fact that backticks always use the shell. You needed modules like IPC::System::Simple to get a safe capture method. Will it be the same in Perl 6?
This is one area of Perl 6 that I wish had been designed with a little more ambition. It pretty much looks like what Perl 5 already had, except with slightly better function names and slightly better exit code handling.
The thing is, what Perl 5 had in this area wasn’t very great.
What I usually want when I need to run a command from a Perl script, is a function that combines all these features:
1) short & easy to use
2) safe parameter passing (a.k.a. no shell)
3) checking whether it was actually run
4) checking its return value, if it was run
5) capturing STDOUT into a string variable
6) suppressing STDERR (this should ideally be an option)
In Perl 5,
–
`...`
does not provide (2).–
system(...)
does not provide (5) and (6), and barely provides (1).–
open(..., "... |")
does not provide (1) and (2).I came up with this custom Perl 5 function which supports all 6 features, and I use it in some of my Linux scripts, but it is probably not cross-platform because it relies on `fork`:
Will I need to go to such lengths in Perl 6 as well, to use external commands in my workflow in a clean and safe way?
Is it too much to asks to have something like this (in a cross-platform way of course) built into the language?
It feels like we could have all this built in, though I’m not sure where to draw the line between the core and a module.
I like your 6 features, though I’d also like to see options for getting at the STDOUT and STDERR as independent file handles, callbacks, or even Supplies.
Ideally I think we’d combine the best features from the perl 5 IPC::Run3 and AnyEvent::Util::run_cmd, and mix it nicely with the perl existing 6 asynchrony.
P.S.:
I really like the “subprocess.check_output” function, which Python introduced in 2.7. It’s so good that it should either be blatantly copied into Perl 6 — or even improved. However Perl 6 should not be missing any features here if the competition offers them :)
I just brought up the topic on the #perl6 IRC channel, and it seems there are some ideas for an even nicer solution than Python’s and PHP’s.
Let’s see if anything comes of it… Perl 6 is still a while off from “official 6.0 release” readiness, so there’s still time… :)
Splitting system() (which might or might not use shell) into run() and shell() is a step in the right direction (in Perl 5 one can use a module, the name of which escapes me ATM). However, I also wonder about execution without shell in qx/qqx, and also about capturing stderr and exit code.