IPC::System::Simple - Cross-platform, multi-arg backticks

pjf on 2008-05-13T01:00:29

IPC::System::Simple released - Cross-platform, multi-arg backticks
One of my greatest itches with Perl is the difficultly in doing things correctly with the system() command, which allows one to run a process and wait for it to finish, and backticks (aka qx()), which allows one to run a process and capture its output.

It's not that these commands are hard to use. system("mount /dev/backup") and I've got my backup media mounted. my $config = qx(ipconfig) and I've got my Windows IP configuration. They're dead easy to use, they're just a pain to tell if they worked.

After running, these commands set a special variable named $?, or $CHILD_ERROR if you've told perl to use English. This variable is designed to be "friendly" to "C programmers", who are familiar with the return value of the wait() system call. But who on earth wants to be familiar with the return value of wait()? After teaching thousands of people Perl over the last seven years, I've only had two people admit to know what the wait() return value looks like, and both of them needed therapy afterwards. It's a packed bit-string, and is grossly unfriendly for anyone to use in any language.

So, the usual state of affairs for many developers is they go through their day using system() and backticks and completely ignoring $?, because it's too hard. Then one day a disaster happens where they discover that all their backups are empty, or all their files are corrupted, or all their furniture has been repossessed, because their calls to system() have been failing for years, but nobody noticed because $? was too ugly to contemplate.

Those developers who have been through this process, or seen other people's lives ruined usually try to take a little more care. They recall that $? will be exactly zero if the process ran to completion and returned a zero exit status, which usually indicates success. Their code becomes littered with statements like $? and die $? or system(...) and die $?. That makes them feel warm and fuzzy until they discover it doesn't work. Their command may legitimately return a range of statuses to indicate success, and a whole bunch of things to indicate failure. Worse still, printing the contents of $? as an error message is worse than useless. Nobody understands what the hell it means; if you did, you wouldn't be printing it.

The end result of all this is that Perl sucks at something it's supposed to be good at; namely firing off system commands and being awesome for system administrators. It is this problem that IPC::System::Simple solves.

Put simply, if you're using IPC::System::Simple, you can replace all your calls to system() with run(), and all your calls to backticks with capture(), and it will all work the same way, except that you'll get incredibly useful messages on error. Got a funny exit status? It'll tell you what it is, and the command that caused it. Killed by a signal? You'll get not just the number, but its name, as well a whether it dumped core. Passed in tainted data? You'll get told what was tainted. And it gets better.

Let's say that you're using rsync to backup your files from an active filesystem. It exits with 0 for good, and 24 for "files went missing". On an active filesystem, files disappearing can be considered normal, so we'd like both of these to be considered 'fine'. Anything else, we'd like to get told. Can we do this with IPC::System::Simple? We sure can! Just provide the list of acceptable exit statuses as the first argument:

run( [0,24], "rsync ....")

IPC::System::Simple's run command also works the way that the Perl security pages say that system() should run. That is, when called with multiple arguments, it bypasses the shell. Perl's regular system() when called with multiple arguments will go and use the shell anyway when on a Windows system, which is bad if you were trying to avoid shell meta-characters.

You can get the same shell-bypassing behaviour with capture(); just pass it in multiple arguments and you're done. This even works under Windows, which normally doesn't even support shell-bypassing pipes, let alone checking your command's return values and formatting your errors afterwards. You even get the full 16-bit Windows exit value made available to you, which is something $? and its dark terrors will never give you.

Best of all, installing IPC::System::Simple is a breeze. It's pure perl, with no dependencies on non-core modules. Even if you have no other way of installing modules, you can just copy the .pm file into an appropriate directory and you're done.

Don't put up with the tragedies and uncertain futures that system() and backticks can bring. Use IPC::System::Simple and make handling your system interactions correctly a painless experience.