When calling directly from perl, as Glenn Jackman says, I would use system or exec to avoid quoting trouble and to capture the output, for example to compute the md5sum of a file:
my $pid, $safe_kid;
die "cannot fork: $!" unless defined ($pid = open($safe_kid, "-|"));
if ($pid == 0) {
# This is the child process, exec md5sum
exec('/usr/bin/md5sum', '--binary', $filename) or die "can't exec md5sum: $!";
} else {
# This is the parent process, read data (we do not need to wait for
# child process termination)
@sum = <$safe_kid>;
close $safe_kid; # $? contains status
}
if ($?!=0) {
die "Problem computing hashsums on '$filename': $!\n";
}
On a related note, I was looking for a way to print command line arguments to the shell for the user to copy and paste. I hit upon the idea of single-quoting everything, and using echo
to recompose the string if a single-quote was already present, although using String::ShellQuote seems to be a better idea, really:
#!/usr/bin/perl
use strict;
testEscapeForBash("Hello World");
testEscapeForBash("Hello World!");
testEscapeForBash("Nothing_special_here.txt");
testEscapeForBash("With'One Single Quote");
testEscapeForBash("With 'Two Single' Quotes");
testEscapeForBash("'With Surrounding Single Quotes'");
testEscapeForBash("With \$ Dollar Sign");
testEscapeForBash("With * Fileglob");
testEscapeForBash("With ! History Expansion Sign");
testEscapeForBash(" ");
testEscapeForBash(" Some surrounding spaces ");
sub testEscapeForBash {
my ($in) = @_;
my $out = escapeForBash($in);
print "[$in] gives\n $out\n";
}
sub escapeForBash {
my ($name) = @_;
if (!$name) {
die "Empty name passed to 'escapeForBash'"
}
my @parts = split(/'/,$name,-1); # limit is negative to catch trailing quote
my $res;
if (@parts == 1) {
$res = "'$name'"
}
elsif (@parts > 1) {
$res = '$(echo ';
my $first = 1;
for my $part (@parts) {
$res .= "\"'\"" unless $first;
$first = 0;
$res .= "'";
$res .= $part;
$res .= "'";
}
$res .= ')';
}
else {
die "Weird number of parts: @parts"
}
return $res
}
Let's run this:
[Hello World] gives
'Hello World'
[Hello World!] gives
'Hello World!'
[Nothing_special_here.txt] gives
'Nothing_special_here.txt'
[With'One Single Quote] gives
$(echo 'With'"'"'One Single Quote')
[With 'Two Single' Quotes] gives
$(echo 'With '"'"'Two Single'"'"' Quotes')
['With Surrounding Single Quotes'] gives
$(echo ''"'"'With Surrounding Single Quotes'"'"'')
[With $ Dollar Sign] gives
'With $ Dollar Sign'
[With * Fileglob] gives
'With * Fileglob'
[With ! History Expansion Sign] gives
'With ! History Expansion Sign'
[ ] gives
' '
[ Some surrounding spaces ] gives
' Some surrounding spaces '