1#!perl -w
2use strict;
3
4# $Id: shell-quote,v 1.3 2010-06-11 20:00:24 roderick Exp $
5#
6# Roderick Schertler <roderick@argon.org>
7
8# Copyright (C) 1999 Roderick Schertler
9#
10# This program is free software; you can redistribute it and/or modify
11# it under the terms of the GNU General Public License as published by
12# the Free Software Foundation; either version 2 of the License, or (at
13# your option) any later version.
14#
15# This program is distributed in the hope that it will be useful,
16# but WITHOUT ANY WARRANTY; without even the implied warranty of
17# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18# General Public License for more details.
19#
20# For a copy of the GNU General Public License write to the Free Software
21# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22
23use String::ShellQuote	qw(shell_quote);
24
25(my $Me		= $0 ne '-e' ? $0 : $^X) =~ s-.*/--;
26my $Debug	= 0;
27my $Exit	= 0;
28my $Version	= q$Revision: 1.3 $ =~ /(\d\S+)/ ? $1 : '?';
29
30my @Option_spec = (
31    'debug!'	=> \$Debug,
32    'help!'	=> sub { usage() },
33    'version'	=> sub { print "$Me version $Version\n"; exit },
34);
35
36my $Usage = <<EOF;
37usage: $Me [switch]...
38switches:
39    --debug	turn debugging on
40    --help	show this and then die
41    --version	show the version ($Version) and exit
42Use \`perldoc $Me\' to see the full documentation.
43EOF
44
45sub debug {
46    print STDERR "debug: ", @_, "\n" if $Debug;
47}
48
49sub usage {
50    warn "$Me: ", @_ if @_;
51    # Use exit() rather than die(), as Getopt::Long does eval().
52    print STDERR $Usage;
53    exit 1;
54}
55
56# This is basically Getopt::Long but it has the defaults set up the way I
57# think they should be.
58
59sub getopt {
60    # Don't bother if there aren't any switches.  This test works because
61    # I'm setting $REQUIRE_ORDER.
62    return 1 unless @ARGV && substr($ARGV[0], 0, 1) eq '-';
63
64    my $bundling = 0;
65    if (@_ && ($_[0] eq -bundle || $_[0] eq -bundling)) {
66	$bundling = 1;
67	shift;
68    }
69
70    {
71	# I'm setting this environment variable when loading Getopt::Long
72	# so that the defaults for options added later (which aren't set
73	# explicitly below) are more likely to match what I'd like.
74	local $ENV{POSIXLY_CORRECT} = 1;
75	require Getopt::Long;
76    }
77
78    Getopt::Long->VERSION(2.19);
79    Getopt::Long::Configure(
80	'no_auto_abbrev',
81	'no_getopt_compat',
82	'require_order',
83	$bundling ? 'bundling' : (),
84	'no_ignore_case',
85	'prefix_pattern=(--|-)',
86    ) if 1;
87
88    # The getopt function puts the vars into its caller's package so
89    # it's necessary to jump to it so that its caller is my caller.
90    #goto &Getopt::Long::GetOptions;
91    Getopt::Long::GetOptions(@_);
92}
93
94sub init {
95    getopt -bundle, @Option_spec or usage if @ARGV;
96}
97
98sub main {
99    init;
100    print shell_quote(@ARGV), "\n"
101	if @ARGV;
102    return 0;
103}
104
105$Exit = main || $Exit;
106$Exit = 1 if $Exit && !($Exit % 256);
107exit $Exit;
108
109__END__
110
111=head1 NAME
112
113shell-quote - quote arguments for safe use, unmodified in a shell command
114
115=head1 SYNOPSIS
116
117B<shell-quote> [I<switch>]... I<arg>...
118
119=head1 DESCRIPTION
120
121B<shell-quote> lets you pass arbitrary strings through the shell so that
122they won't be changed by the shell.  This lets you process commands or
123files with embedded white space or shell globbing characters safely.
124Here are a few examples.
125
126=head1 EXAMPLES
127
128=over
129
130=item B<ssh preserving args>
131
132When running a remote command with ssh, ssh doesn't preserve the separate
133arguments it receives.  It just joins them with spaces and passes them to
134C<$SHELL -c>.  This doesn't work as intended:
135
136    ssh host touch 'hi there'		# fails
137
138It creates 2 files, F<hi> and F<there>.  Instead, do this:
139
140    cmd=`shell-quote touch 'hi there'`
141    ssh host "$cmd"
142
143This gives you just 1 file, F<hi there>.
144
145=item B<process find output>
146
147It's not ordinarily possible to process an arbitrary list of files
148output by B<find> with a shell script.  Anything you put in $IFS to
149split up the output could legitimately be in a file's name.  Here's how
150you can do it using B<shell-quote>:
151
152    eval set -- `find -type f -print0 | xargs -0 shell-quote --`
153
154=item B<debug shell scripts>
155
156B<shell-quote> is better than B<echo> for debugging shell scripts.
157
158    debug() {
159    	[ -z "$debug" ] || shell-quote "debug:" "$@"
160    }
161
162With B<echo> you can't tell the difference between C<debug 'foo bar'>
163and C<debug foo bar>, but with B<shell-quote> you can.
164
165=item B<save a command for later>
166
167B<shell-quote> can be used to build up a shell command to run later.
168Say you want the user to be able to give you switches for a command
169you're going to run.  If you don't want the switches to be re-evaluated
170by the shell (which is usually a good idea, else there are things the
171user can't pass through), you can do something like this:
172
173    user_switches=
174    while [ $# != 0 ]
175    do
176    	case x$1 in
177    	    x--pass-through)
178	    	[ $# -gt 1 ] || die "need an argument for $1"
179	    	user_switches="$user_switches "`shell-quote -- "$2"`
180    	    	shift;;
181    	    # process other switches
182    	esac
183    	shift
184    done
185    # later
186    eval "shell-quote some-command $user_switches my args"
187
188=back
189
190=head1 OPTIONS
191
192=over 4
193
194=item B<--debug>
195
196Turn debugging on.
197
198=item B<--help>
199
200Show the usage message and die.
201
202=item B<--version>
203
204Show the version number and exit.
205
206=back
207
208=head1 AVAILABILITY
209
210The code is licensed under the GNU GPL.  Check
211http://www.argon.org/~roderick/ or CPAN for updated versions.
212
213=head1 AUTHOR
214
215Roderick Schertler <roderick@argon.org>
216
217=cut
218