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