How to break a bash command into an array consisting of the argumentsin the command?


P

Peng Yu

Hi,

Suppose that I have a bash command in a string, e.g.

cmd.sh a 'a b' '
'

I want get an array consisting of "cmd.sh" "a" "a b" "\n". Is there a robust way to do so in perl that can handle all the possible cases? Thanks.
 
Ad

Advertisements

J

Jürgen Exner

Peng Yu said:
Suppose that I have a bash command in a string, e.g.

cmd.sh a 'a b' '
'

I want get an array consisting of "cmd.sh" "a" "a b" "\n". Is there a robust way to do so in perl that can handle all the possible cases? Thanks.

A brief glance at a BASH EBNF shows:

<command> ::= <simple_command>
| <shell_command>
| <shell_command> <redirection_list>

<shell_command> ::= <for_command>
| <case_command>
| while <compound_list> do <compound_list> done
| until <compound_list> do <compound_list> done
| <select_command>
| <if_command>
| <subshell>
| <group_command>
| <function_def>

Somewhat simplified: a command can be rather complex, in particular it
can be recursive and it can contain pretty much any BASH element
whatsoever. And that implies that the answer to your question is:
Of course there is a robust way to write a fully-featured BASH-parser in
Perl. And nothing short of a fully-featured BASH-parser will be able to
parse a BASH command line.

jue
 
R

Rainer Weikusat

Peng Yu said:
Suppose that I have a bash command in a string, e.g.

cmd.sh a 'a b' '
'

I want get an array consisting of "cmd.sh" "a" "a b" "\n". Is there
a robust way to do so in perl that can handle all the possible
cases? Thanks.

Do you consider command substitution a possible case? And what about
process substitution? In case process substitution isn't needed, a
reasonably simple idea would be to invoke the shell to let it perform
"word-splitting" on the command string and use perl to transport the
result of that to a 'parent perl' with the help of some 'suitable
encoding', eg

--------------------
sub shell_cmd_to_list
{
my ($all, @l, $one, $pos);

$all = `perl -e 'my \$v; \$v .= pack("Z*", \$_) for \@ARGV; print \$v' $_[0]`;
$pos = 0;
do {
$one = unpack('@'.$pos.'Z*', $all);
push(@l, $one);
$pos += length($one) + 1;
} while ($pos < length($all));

return @l;
}

my $cmd = "a 'a b' 'a\nb' \"`ls /`\"";
my @v = shell_cmd_to_list($cmd);

printf("%u\n--\n%s\n\n", $_, $v[$_]) for (0 .. $#v);
 
Ad

Advertisements

R

Rainer Weikusat

Rainer Weikusat said:
Peng Yu said:
Suppose that I have a bash command in a string, e.g.

cmd.sh a 'a b' '
'

I want get an array consisting of "cmd.sh" "a" "a b" "\n". Is there
a robust way to do so in perl that can handle all the possible
cases?
[...]

sub shell_cmd_to_list
{
my ($all, @l, $one, $pos);

$all = `perl -e 'my \$v; \$v .= pack("Z*", \$_) for \@ARGV; print \$v' $_[0]`;
$pos = 0;
do {
$one = unpack('@'.$pos.'Z*', $all);
push(@l, $one);
$pos += length($one) + 1;
} while ($pos < length($all));

return @l;
}

Coming to think of this, this should probably rather be

----------------
sub shell_cmd_to_list
{
my ($all, @l, $one, $pos);

$all = `perl -e 'my \$v; \$v .= pack("Z*", \$_) for \@ARGV; print \$v' $_[0]`;
$? and return ();

$pos = 0;
while ($pos < length($all)) {
$one = unpack('@'.$pos.'Z*', $all);
push(@l, $one);
$pos += length($one) + 1;
}

return @l;
}
-----------------

which will return an empty list in case the shell encountered a synax
error in the argument string (alternatively, an exception could be
thrown) or if there were no arguments.

It should also be noted that this will not only perform command
substitution aka 'run arbitrary commands contained in the argument
string' but will also run an arbitrary 'trailing shell script'
attached to $_[0], IOW, it is completely unsuitable for processing
input from untrusted sources. OTOH, the shell already knows how to
parse 'shell commands' and using it to do this instead of
reprogramming the parser in Perl is IMHO generally sensible.

Special note: This is one of the rare cases where initializing a
variable is actually necessary in Perl because '@Z*' is not the same
as '@0Z*'.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top