—————# $Id: ShellQuote.pm,v 1.11 2010-06-11 20:08:57 roderick Exp $
#
# Copyright (c) 1997 Roderick Schertler. All rights reserved. This
# program is free software; you can redistribute it and/or modify it
# under the same terms as Perl itself.
=head1 NAME
String::ShellQuote - quote strings for passing through the shell
=head1 SYNOPSIS
$string = shell_quote @list;
$string = shell_quote_best_effort @list;
$string = shell_comment_quote $string;
=head1 DESCRIPTION
This module contains some functions which are useful for quoting strings
which are going to pass through the shell or a shell-like object.
=over
=cut
package
String::ShellQuote;
use
strict;
require
Exporter;
$VERSION
=
'1.04'
;
@ISA
=
qw(Exporter)
;
@EXPORT
=
qw(shell_quote shell_quote_best_effort shell_comment_quote)
;
sub
croak {
goto
&Carp::croak
;
}
sub
_shell_quote_backend {
my
@in
=
@_
;
my
@err
= ();
if
(0) {
RS::Handy::data_dump(\
@in
);
}
return
\
@err
,
''
unless
@in
;
my
$ret
=
''
;
my
$saw_non_equal
= 0;
foreach
(
@in
) {
if
(!
defined
$_
or
$_
eq
''
) {
$_
=
"''"
;
next
;
}
if
(s/\x00//g) {
push
@err
,
"No way to quote string containing null (\\000) bytes"
;
}
my
$escape
= 0;
# = needs quoting when it's the first element (or part of a
# series of such elements), as in command position it's a
# program-local environment setting
if
(/=/) {
if
(!
$saw_non_equal
) {
$escape
= 1;
}
}
else
{
$saw_non_equal
= 1;
}
if
(m|[^\w!%+,\-./:=@^]|) {
$escape
= 1;
}
if
(
$escape
|| (!
$saw_non_equal
&& /=/)) {
# ' -> '\''
s/
'/'
\\
''
/g;
# make multiple ' in a row look simpler
# '\'''\'''\'' -> '"'''"'
s|((?:
'\\'
'){2,})|q{'
"} . (q{'} x (length($1) / 4)) . q{"
'}|ge;
$_
=
"'$_'"
;
s/^
''
//;
s/
''
$//;
}
}
continue
{
$ret
.=
"$_ "
;
}
chop
$ret
;
return
\
@err
,
$ret
;
}
=item B<shell_quote> [I<string>]...
B<shell_quote> quotes strings so they can be passed through the shell.
Each I<string> is quoted so that the shell will pass it along as a
single argument and without further interpretation. If no I<string>s
are given an empty string is returned.
If any I<string> can't be safely quoted B<shell_quote> will B<croak>.
=cut
sub
shell_quote {
my
(
$rerr
,
$s
) = _shell_quote_backend
@_
;
if
(
@$rerr
) {
my
%seen
;
@$rerr
=
grep
{ !
$seen
{
$_
}++ }
@$rerr
;
my
$s
=
join
''
,
map
{
"shell_quote(): $_\n"
}
@$rerr
;
chomp
$s
;
croak
$s
;
}
return
$s
;
}
=item B<shell_quote_best_effort> [I<string>]...
This is like B<shell_quote>, excpet if the string can't be safely quoted
it does the best it can and returns the result, instead of dying.
=cut
sub
shell_quote_best_effort {
my
(
$rerr
,
$s
) = _shell_quote_backend
@_
;
return
$s
;
}
=item B<shell_comment_quote> [I<string>]
B<shell_comment_quote> quotes the I<string> so that it can safely be
included in a shell-style comment (the current algorithm is that a sharp
character is placed after any newlines in the string).
This routine might be changed to accept multiple I<string> arguments
in the future. I haven't done this yet because I'm not sure if the
I<string>s should be joined with blanks ($") or nothing ($,). Cast
your vote today! Be sure to justify your answer.
=cut
sub
shell_comment_quote {
return
''
unless
@_
;
unless
(
@_
== 1) {
croak
"Too many arguments to shell_comment_quote "
.
"(got "
.
@_
.
" expected 1)"
;
}
local
$_
=
shift
;
s/\n/\n
#/g;
return
$_
;
}
1;
__END__
=back
=head1 EXAMPLES
$cmd = 'fuser 2>/dev/null ' . shell_quote @files;
@pids = split ' ', `$cmd`;
print CFG "# Configured by: ",
shell_comment_quote($ENV{LOGNAME}), "\n";
=head1 BUGS
Only Bourne shell quoting is supported. I'd like to add other shells
(particularly cmd.exe), but I'm not familiar with them. It would be a
big help if somebody supplied the details.
=head1 AUTHOR
Roderick Schertler <F<roderick@argon.org>>
=head1 SEE ALSO
perl(1).
=cut