Eule has asked for the wisdom of the Perl Monks concerning the following question:

Hi, for security reasons I am trying to switch the execution of other commands from backticks to the list-form of open.

Before:

$result = `$cmd $args`;
After:
open ($fh, '-|', $cmd_file, @args); $test_result = <$fh>;
As you might have noticed, the latter form needs the arguments splitted up into a list, the former one just a plain string. For simple arguments of course 'split' will do the trick. But what about arguments that contain white space? If $args is something like
-w 70 -c 80 -r 'bla bla'
a simple split at whitespace would certainly produce a wrong argument list. Now the shell could of course do this, but the point of removing the backticks was to get rid of the shell in the first place.

So my question: How can I split up a string into an argument list just as the shell would do it? Including escape characters like ' " \ ...

Thanks,

Christopher

Replies are listed 'Best First'.
Re: Switching from backticks to open
by repellent (Priest) on Nov 03, 2008 at 08:45 UTC
    For best practice, you'll always want to keep your arguments in array-form. For instance, keep @ARGV as-is, and pass it to system, exec, open, etc. as a LIST. This will ensure you're handling arguments correctly. You'll find that any other way is less-than-ideal and prone to problems.

    With that said, let's say you already have your arguments as a string. To emulate shell parsing, in order to get back an array of arguments:
    use Text::ParseWords; # core module my $args = q(-w 70 -c 80 -r 'bla bla'); my @ARGS = shellwords($args); # this is how to keep single-quotes # [but it's probably not what you want for open()] my @ARGS2 = parse_line('\s+', 1, $args);

    See Text::ParseWords.

      Using Text::ParseWords will introduce some incompatibilities.

      • shellwords parses the text in a manner "similar to most Unix shells". Backticks use the bourne shell, not "most unix shells".

      • Redirection and other shell instructions are silently treated as arguments instead of resulting in an error.

      • Environment and special variables won't be interpolated.

      • Globs won't be expanded.

      Update: Added last item.

        Yes, with Text::ParseWords, we'll lose all "shell-magic" interpretation. That means no $SHELL_VARIABLE expansion, globs/redirection/pipe operators are treated as plain arguments, etc.

        shellwords($text) is merely parsing $text into words as-is. Care should be taken to manually expand globs or $SHELL_VARIABLES embedded in the literal $text. Caution: Beyond the very simple case, it is best to avoid going down the path of recreating shell behavior using shellwords().
      Thanks - that was exactly the missing part!
Re: Switching from backticks to open
by ikegami (Patriarch) on Nov 03, 2008 at 09:10 UTC
    open($fh, '-|', ...) is no more secure than backticks if the command has no args. Have a look at IPC::System::Simple's capturex.