and now for something completely different...

sometimes in a shell script you need to do stuff that modifies the positional parameters ($@, $1, $2, etc) from the cmdline, usually via the builtin shift, maybe from using getopt(1) or similar.

for example, it's quite common to see the following in a shell script that parses cmdline flags with getopt

# parse cmdline options with getopt
TEMP=`getopt -o h123 -n $0 -- "$@"`
if [ $? != 0 ] ; then usage >&2 ; exit 1 ; fi
# overwrites $@ with getopt-generated/sanitized arguments
eval set -- "$TEMP"
# destructively iterates across $@
while true; do
  case "$1" in
        -h)
          # handle option h
          shift
        ;;
        ...
        --)
          shift
          break
        ;;
      esac
...
done

but sometimes you want to be able to restore the original positional parameters later, or otherwise quote them in a manner where they can be passed along to another script/function, log them, embed them in a string/file, etc. you can't always just stash them in another variable to use that later, because "$@" has a special meaning to the shell that can't otherwise be reproduced with another variable.

one way you could try to store/save them would be to save the original copy of $@ and use eval set -- later on to change it back to the saved variable. using a slightly simplified version of the above:

#!/bin/sh

# save the cmdline
saved="$@"

echo original command line, $# arguments: "$@"

while test -n "$*"; do echo arg: "$1"; shift; done
echo mangled command line, $# arguments: "$@"

# restore the cmdline
eval set -- "$saved"
echo restored command line, $# arguments: "$@"
while test -n "$*"; do echo arg: "$1"; shift; done

and at first glance, it seems to work:

rangda[/home/sean] ./bad.sh one two three
original command line, 3 arguments: one two three
arg: one
arg: two
arg: three
mangled command line, 0 arguments:
restored command line, 3 arguments: one two three
arg: one
arg: two
arg: three

but you may run into problems when some parameters have spaces, or quotes:

rangda[/home/sean] ./bad.sh "1: one" "2: two"
original command line, 2 arguments: 1: one 2: two
arg: 1: one
arg: 2: two
mangled command line, 0 arguments:
restored command line, 4 arguments: 1: one 2: two
arg: 1:
arg: one
arg: 2:
arg: two

rangda[/home/sean] ./bad.sh "one's one"
original command line, 1 arguments: one's one
arg: one's one
mangled command line, 0 arguments:
./bad.sh: eval: line 12: unexpected EOF while looking for matching `''

or worse, shell meta characters:

rangda[/home/sean] ./bad.sh 'i wonder what `pwd` would do'
original command line, 1 arguments: i wonder what `pwd` would do
arg: i wonder what `pwd` would do
mangled command line, 0 arguments:
restored command line, 6 arguments: i wonder what /home/sean would do
arg: i
arg: wonder
arg: what
arg: /home/sean
arg: would
arg: do

the solution is to build up the saved value a bit more manually, single-quoting each parameter (which should prevent any unintended side effects of space/quotes/metacharacters), and escaping existing single quotes inside of parameters.

# escape any single quotes in an argument
quote(){
  echo "$1" | sed -e "s,','\\\\'',g"
}

# save up a properly quoted/escaped version of "$@"
for arg in "$@"; do
  saved="${saved:+$saved }'$(quote "$arg")'"
done

echo original command line, $# arguments: "$@"

while test -n "$*"; do echo arg: "$1"; shift; done
echo mangled command line, $# arguments: "$@"

# restore the cmdline
eval set -- "$saved"
echo restored command line, $# arguments: "$@"
while test -n "$*"; do echo arg: "$1"; shift; done

let's see how it behaves:

rangda[/home/sean] ./good.sh "1 one" "2: two"
original command line, 2 arguments: 1 one 2: two
arg: 1 one
arg: 2: two
mangled command line, 0 arguments:
restored command line, 2 arguments: 1 one 2: two
arg: 1 one
arg: 2: two

rangda[/home/sean] ./good.sh "one's one"
original command line, 1 arguments: one's one
arg: one's one
mangled command line, 0 arguments:
restored command line, 1 arguments: one's one
arg: one's one

rangda[/home/sean] ./good.sh 'i wonder what `pwd` would do'
original command line, 1 arguments: i wonder what `pwd` would do
arg: i wonder what `pwd` would do
mangled command line, 0 arguments:
restored command line, 1 arguments: i wonder what `pwd` would do
arg: i wonder what `pwd` would do

while not entirely pretty, it does to do the trick. i'd be interested to know if anyone has a better way of doing this (or if there are hidden problems in what i've posted above).

Comment by 669.666666666667 Wed Dec 23 06:49:35 2009
rent a car 6927
Comment by 669.666666666667 Wed Dec 23 07:25:46 2009
Comment by Neo Sun Dec 27 05:40:16 2009
Comment by 669.666666666667 Fri Jan 1 09:21:17 2010
Comment by 669.666666666667 Fri Jan 1 21:50:55 2010
Comment by Hero Fri Jan 8 09:57:15 2010
Comment by Hero Fri Jan 8 09:59:28 2010
Comment by Hero Fri Jan 8 09:59:59 2010
Comment by Dominic Fri Jan 8 15:28:23 2010
Comment by Kir Mon Jan 11 16:33:19 2010
Comment by Aron Thu Jan 14 15:49:34 2010
Comment by Halo Thu Jan 14 15:55:41 2010
Comment by Halo Thu Jan 14 16:19:21 2010
Comment by Halo Thu Jan 14 16:25:49 2010
Comment by Halo Thu Jan 14 16:32:06 2010
Comment by Jane Thu Jan 14 16:56:13 2010
Comment by Dominic Thu Jan 14 17:02:09 2010
Comment by Dominic Thu Jan 14 17:05:21 2010
Comment by Arnie Thu Jan 14 17:11:55 2010
Comment by Arnie Thu Jan 14 17:28:13 2010
Comment by Hero Thu Jan 21 00:55:05 2010
Very nice site!
Comment by Pharmb297 Wed Jan 27 23:50:19 2010
Add a comment