[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: how to do "$@"?
From: |
Akim Demaille |
Subject: |
Re: how to do "$@"? |
Date: |
18 Apr 2002 14:33:03 +0200 |
User-agent: |
Gnus/5.0808 (Gnus v5.8.8) XEmacs/21.4 (Common Lisp) |
| > Date: Wed, 17 Apr 2002 11:41:25 -0600 (MDT)
| > From: "Nelson H. F. Beebe" <address@hidden>
| >
| > The problem seems to arise from manipulations of "$@" that cause
| > quoting of arguments containing blanks to be lost.
|
| I think the main problem is dealing with the case where there are no
| positional arguments. In that case, the Unix Version 7 shell treats
| "$@" like "", whereas POSIX requires that it should treat "$@" as if
| it were absent.
Aah! So that's finally the bottom line!
| For example:
| set x
| shift
| cat "$@" /dev/null
|
| In Unix Version 7, this fails and outputs something like this:
|
| cat: : No such file or directory
|
| whereas in a POSIX shell, it outputs nothing.
|
| > I suspect that most scripts only require the simpler use, and thus,
| > that "$@" should be preferred over the ${1+"$@"} form
|
| But this won't work on older versions of Digital Unix.
|
| How about the following (untested) patch instead? It avoids the
| problem entirely, by removing all instances of "$@" in cases where
| there might be zero positional arguments.
I think it is a good thing to offer the choice to the users: the
documentation should show the two possibilities (using ${1+"$@"} and
having zsh do the right thing), or completely work around using "$@"
when $#=0. The problem with the latter is that is much less `local'
than a plain ${1+"$@"}. Although ugly, this solution, at least, had
the pleasant property of being quite discrete (no if/fi wrapping
needed etc.).
| Index: bin/autoconf.as
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/autoconf.as,v
| retrieving revision 1.1
| diff -p -u -r1.1 autoconf.as
| --- bin/autoconf.as 10 Apr 2002 15:58:19 -0000 1.1
| +++ bin/autoconf.as 17 Apr 2002 21:47:49 -0000
| @@ -185,7 +185,7 @@ test -z "$outfile" && outfile=-
| # Running autom4te.
| run_autom4te="$AUTOM4TE --language=autoconf --output=$outfile"
| # Autom4te expansion.
| -eval set dummy "$traces"
| +eval set dummy "$traces" \$infile
| shift
| -$verbose "$me: running $run_autom4te "${1+"$@"}" $infile" >&2
| -exec $run_autom4te ${1+"$@"} $infile
| +$verbose "$me: running $run_autom4te $*" >&2
| +exec $run_autom4te "$@"
Err, this is not correct: infile might be empty, when using autoconf
in a pipe (hm, which seems to be broken now BTW).
| Index: bin/autoheader.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/autoheader.in,v
| retrieving revision 1.115
| diff -p -u -r1.115 autoheader.in
| --- bin/autoheader.in 8 Mar 2002 11:46:31 -0000 1.115
| +++ bin/autoheader.in 17 Apr 2002 21:47:49 -0000
| @@ -2,7 +2,7 @@
| # -*- Perl -*-
| # @configure_input@
|
| -eval 'exec @PERL@ -S $0 ${1+"$@"}'
| +eval 'case $# in 0) exec @PERL@ -S $0;; *) exec @PERL@ -S $0 "$@";; esac'
| if 0;
|
| # autoheader -- create `config.h.in' from `configure.ac'
| Index: bin/autom4te.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/autom4te.in,v
| retrieving revision 1.59
| diff -p -u -r1.59 autom4te.in
| --- bin/autom4te.in 8 Mar 2002 12:01:23 -0000 1.59
| +++ bin/autom4te.in 17 Apr 2002 21:47:49 -0000
| @@ -2,7 +2,7 @@
| # -*- perl -*-
| # @configure_input@
|
| -eval 'exec @PERL@ -S $0 ${1+"$@"}'
| +eval 'case $# in 0) exec @PERL@ -S $0;; *) exec @PERL@ -S $0 "$@";; esac'
| if 0;
|
| # autom4te - Wrapper around M4 libraries.
| Index: bin/autoreconf.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/autoreconf.in,v
| retrieving revision 1.92
| diff -p -u -r1.92 autoreconf.in
| --- bin/autoreconf.in 5 Apr 2002 09:42:49 -0000 1.92
| +++ bin/autoreconf.in 17 Apr 2002 21:47:50 -0000
| @@ -2,7 +2,7 @@
| # -*- perl -*-
| # @configure_input@
|
| -eval 'exec @PERL@ -S $0 ${1+"$@"}'
| +eval 'case $# in 0) exec @PERL@ -S $0;; *) exec @PERL@ -S $0 "$@";; esac'
| if 0;
|
| # autoreconf - install the GNU Build System in a directory tree
| Index: bin/autoscan.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/autoscan.in,v
| retrieving revision 1.76
| diff -p -u -r1.76 autoscan.in
| --- bin/autoscan.in 19 Mar 2002 15:25:26 -0000 1.76
| +++ bin/autoscan.in 17 Apr 2002 21:47:50 -0000
| @@ -20,7 +20,7 @@
|
| # Written by David MacKenzie <address@hidden>.
|
| -eval 'exec @PERL@ -S $0 ${1+"$@"}'
| +eval 'case $# in 0) exec @PERL@ -S $0;; *) exec @PERL@ -S $0 "$@";; esac'
| if 0;
|
| BEGIN
| Index: bin/autoupdate.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/autoupdate.in,v
| retrieving revision 1.31
| diff -p -u -r1.31 autoupdate.in
| --- bin/autoupdate.in 8 Mar 2002 11:46:31 -0000 1.31
| +++ bin/autoupdate.in 17 Apr 2002 21:47:50 -0000
| @@ -21,7 +21,7 @@
| # Originally written by David MacKenzie <address@hidden>.
| # Rewritten by Akim Demaille <address@hidden>.
|
| -eval 'exec @PERL@ -S $0 ${1+"$@"}'
| +eval 'case $# in 0) exec @PERL@ -S $0;; *) exec @PERL@ -S $0 "$@";; esac'
| if 0;
|
| BEGIN
| Index: bin/ifnames.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/bin/ifnames.in,v
| retrieving revision 1.25
| diff -p -u -r1.25 ifnames.in
| --- bin/ifnames.in 8 Mar 2002 11:46:31 -0000 1.25
| +++ bin/ifnames.in 17 Apr 2002 21:47:50 -0000
| @@ -2,7 +2,7 @@
| # -*- perl -*-
| # @configure_input@
|
| -eval 'exec @PERL@ -S $0 ${1+"$@"}'
| +eval 'case $# in 0) exec @PERL@ -S $0;; *) exec @PERL@ -S $0 "$@";; esac'
| if 0;
|
| # ifnames - print the identifiers used in C preprocessor conditionals
All these seem fine to me.
| Index: config/missing
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/config/missing,v
| retrieving revision 1.6
| diff -p -u -r1.6 missing
| --- config/missing 24 Jan 2002 17:42:05 -0000 1.6
| +++ config/missing 17 Apr 2002 21:47:50 -0000
| @@ -293,23 +293,23 @@ WARNING: \`$1' is missing on your system
| # Look for gnutar/gtar before invocation to avoid ugly error
| # messages.
| if (gnutar --version > /dev/null 2>&1); then
| - gnutar ${1+"$@"} && exit 0
| + gnutar "$@" && exit 0
| fi
| if (gtar --version > /dev/null 2>&1); then
| - gtar ${1+"$@"} && exit 0
| + gtar "$@" && exit 0
| fi
| firstarg="$1"
| if shift; then
| case "$firstarg" in
| *o*)
| firstarg=`echo "$firstarg" | sed s/o//`
| - tar "$firstarg" ${1+"$@"} && exit 0
| + tar "$firstarg" "$@" && exit 0
| ;;
| esac
| case "$firstarg" in
| *h*)
| firstarg=`echo "$firstarg" | sed s/h//`
| - tar "$firstarg" ${1+"$@"} && exit 0
| + tar "$firstarg" "$@" && exit 0
| ;;
| esac
| fi
missing belongs to Automake.
| Index: doc/autoconf.texi
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/doc/autoconf.texi,v
| retrieving revision 1.610
| diff -p -u -r1.610 autoconf.texi
| --- doc/autoconf.texi 11 Apr 2002 10:24:22 -0000 1.610
| +++ doc/autoconf.texi 17 Apr 2002 21:47:54 -0000
| @@ -8458,13 +8458,16 @@ strings inside double-quoted backquoted
| @item $@@
| @cindex @samp{"$@@"}
| One of the most famous shell-portability issues is related to
| address@hidden"$@@"}: when there are no positional arguments, it is supposed
to
| -be equivalent to nothing. But some shells, for instance under Digital
| -Unix 4.0 and 5.0, will then replace it with an empty argument. To be
| -portable, use @address@hidden"$@@"@}}.
| -
| -But that's not the end of the story. Zsh (3.x and 4.x), when emulating
| -the Bourne shell, does perform word splitting on @address@hidden"$@@"@}}...
| address@hidden"$@@"}: when there are no positional arguments, @sc{posix} says
| +that it is supposed to be equivalent to nothing. But the original
| +Unix Version 7 Bourne shell treated it as equivalent to @samp{""}
| +instead, and this behavior survives in some more modern
| +implementations, for instance under Digital Unix 5.0.
| +
| +The traditional way to work around this portability problem is to use
| address@hidden@{1+"$@@"@}}. Unfortunately this method does not work with
| +Zsh (3.x and 4.x), which is used on Mac OS X. When emulating
| +the Bourne shell, Zsh performs word splitting on @address@hidden"$@@"@}}:
|
| @example
| zsh $ @kbd{emulate sh}
| @@ -8478,19 +8481,25 @@ World
| @end example
|
| @noindent
| -It is not clear whether this is a violation of the Bourne shell
| -standard, nevertheless, in this regard Zsh is different from all the
| -other shells. Of course Zsh handles @samp{"$@@"} properly, but we can't
| -use it portably...
| -
| -Fortunately, there is a workaround which relies on Zsh's ``global
| -aliases'': let it convert @address@hidden"$@@"@}} into @samp{"$@@"} by
| -itself:
| +Zsh handles plain @samp{"$@@"} properly, but we can't use plain
| address@hidden"$@@"} because of the portability problems mentioned above.
| +
| +The most portable workaround is to avoid @samp{"$@@"} if it is possible
| +that there may be no positional arguments. For example, instead of:
|
| @example
| -test -n "address@hidden@}" = set && alias -g 'address@hidden"$@@"@}'='"$@@"'
| +# Not portable if there are no positional arguments.
| +cat conftest.c "$@@"
| @end example
|
| +you should use this instead:
| +
| address@hidden
| +case $# in
| +0) cat conftest.c;;
| +*) cat conftest.c "$@@";;
| +esac
| address@hidden example
|
| @item address@hidden@var{var}:address@hidden@}
| @c Info cannot handle `:' in index entries.
I would really prefer that we preserve the choice. Personally, I much
prefer using ${1+"$@"} for the reasons I gave above.
| Index: lib/autoconf/programs.m4
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/lib/autoconf/programs.m4,v
| retrieving revision 1.9
| diff -p -u -r1.9 programs.m4
| --- lib/autoconf/programs.m4 2 Mar 2002 15:19:48 -0000 1.9
| +++ lib/autoconf/programs.m4 17 Apr 2002 21:47:54 -0000
| @@ -92,7 +92,10 @@ m4_ifvaln([$6],
| # However, it has the same basename, so the bogon will be chosen
| # first if we set $1 to just the basename; use the full file name.
| shift
| - set dummy "$as_dir/$ac_word" ${1+"address@hidden"}
| + case address@hidden:@] in
| + 0) set dummy "$as_dir/$ac_word";;
| + *) set dummy "$as_dir/$ac_word" "address@hidden";;
| + esac
| shift
| ac_cv_prog_$1="address@hidden"
| m4_if([$2], [$4],
This is the kind of obfuscation I'd like to avoid.
| Index: lib/autoconf/status.m4
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/lib/autoconf/status.m4,v
| retrieving revision 1.22
| diff -p -u -r1.22 status.m4
| --- lib/autoconf/status.m4 10 Apr 2002 15:58:20 -0000 1.22
| +++ lib/autoconf/status.m4 17 Apr 2002 21:47:55 -0000
| @@ -1383,7 +1383,10 @@ do
| ac_option=`expr "x$[1]" : 'x\([[^=]]*\)='`
| ac_optarg=`expr "x$[1]" : 'x[[^=]]*=\(.*\)'`
| shift
| - set dummy "$ac_option" "$ac_optarg" ${1+"address@hidden"}
| + case $[#] in
| + 0) set dummy "$ac_option" "$ac_optarg";;
| + *) set dummy "$ac_option" "$ac_optarg" "address@hidden";;
| + esac
| shift
| ;;
| -*);;
Likewise.
| Index: lib/autotest/general.m4
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/lib/autotest/general.m4,v
| retrieving revision 1.130
| diff -p -u -r1.130 general.m4
| --- lib/autotest/general.m4 10 Apr 2002 15:58:20 -0000 1.130
| +++ lib/autotest/general.m4 17 Apr 2002 21:47:55 -0000
| @@ -101,7 +101,7 @@ AS_PREPARE
| SHELL=${CONFIG_SHELL-/bin/sh}
|
| # How were we run?
| -at_cli_args=${1+"address@hidden"}
| +at_cli_args="address@hidden"
|
| # Load the config file.
| for at_file in atconfig atlocal
| @@ -570,8 +570,12 @@ _ATEOF
| {
| echo "#! /bin/sh"
| echo "cd $at_dir"
| - echo 'exec ${CONFIG_SHELL-'"$SHELL"'}' "$[0]" \
| - '-v -d' "$at_debug_args" "$at_group"
'${1+"address@hidden"}'
| + echo 'case $[#] in'
| + echo '0) exec ${CONFIG_SHELL-'"$SHELL"'}' "$[0]" \
| + '-v -d' "$at_debug_args" "$at_group;;"
| + echo '*) exec ${CONFIG_SHELL-'"$SHELL"'}' "$[0]" \
| + '-v -d' "$at_debug_args" "$at_group" '"address@hidden";;'
| + echo 'esac'
| echo 'exit 1'
| } >$at_group_dir/run
| chmod +x $at_group_dir/run
Ditto.
| Index: lib/m4sugar/m4sh.m4
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/lib/m4sugar/m4sh.m4,v
| retrieving revision 1.83
| diff -p -u -r1.83 m4sh.m4
| --- lib/m4sugar/m4sh.m4 10 Apr 2002 17:36:45 -0000 1.83
| +++ lib/m4sugar/m4sh.m4 17 Apr 2002 21:47:55 -0000
| @@ -146,9 +146,6 @@ m4_defun([AS_SHELL_SANITIZE],
| if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
| emulate sh
| NULLCMD=:
| - [#] Zsh 3.x and 4.x performs word splitting on ${1+"address@hidden"}, which
| - # is contrary to our usage. Disable this feature.
| - alias -g '${1+"address@hidden"}'='"address@hidden"'
| elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
| set -o posix
| fi
I see no point is removing this: it makes M4sh scripts more uniform,
more robust to the usual writing. Even if you want to promote the
case $#/"$@" approach, I don't understand what motivates the removal
of this.
| @@ -555,7 +552,10 @@ _AS_LINENO_WORKS || {
| AS_UNSET(ENV)
| CONFIG_SHELL=$as_dir/$as_base
| export CONFIG_SHELL
| - exec "$CONFIG_SHELL" "$[0]" ${1+"address@hidden"}
| + case $[#] in
| + 0) exec "$CONFIG_SHELL" "$[0]";;
| + *) exec "$CONFIG_SHELL" "$[0]" "address@hidden";;
| + esac
| fi;;
| esac
| done]);;
:(
| Index: tests/atgeneral.m4
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/tests/atgeneral.m4,v
| retrieving revision 1.70
| diff -p -u -r1.70 atgeneral.m4
| --- tests/atgeneral.m4 25 Nov 2001 15:13:04 -0000 1.70
| +++ tests/atgeneral.m4 17 Apr 2002 21:47:55 -0000
| @@ -318,7 +318,10 @@ elif test $at_debug = false; then
| for at_group in $at_fail_list; do
| echo $at_n " $at_group$at_c"
| ( echo "#! /bin/sh"
| - echo 'exec ${CONFIG_SHELL-'"$SHELL"'} '"$[0]"' -v -d '"$at_group"'
${1+"address@hidden"}'
| + echo 'case $[#] in'
| + echo '0) exec ${CONFIG_SHELL-'"$SHELL"'} '"$[0]"' -v -d
'"$at_group"';;'
| + echo '*) exec ${CONFIG_SHELL-'"$SHELL"'} '"$[0]"' -v -d '"$at_group"'
"address@hidden";;'
| + echo 'esac'
| echo 'exit 1'
| ) >debug-$at_group.sh
| chmod +x debug-$at_group.sh
:(
Please, install.
| Index: tests/autoreconf.in
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/tests/autoreconf.in,v
| retrieving revision 1.2
| diff -p -u -r1.2 autoreconf.in
| --- tests/autoreconf.in 14 Dec 2001 17:57:29 -0000 1.2
| +++ tests/autoreconf.in 17 Apr 2002 21:47:55 -0000
| @@ -3,4 +3,7 @@
|
| me=`echo "$0" | sed -e 's,.*[\\/],,'`
|
| -exec @abs_top_builddir@/bin/$me --autoconf-dir @abs_top_builddir@/lib
${1+"$@"}
| +case $# in
| +0) exec @abs_top_builddir@/bin/$me --autoconf-dir @abs_top_builddir@/lib;;
| +*) exec @abs_top_builddir@/bin/$me --autoconf-dir @abs_top_builddir@/lib
"$@";;
| +esac
This guy is generated (and doesn't reflect some of your other changes).
| Index: tests/c.at
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/tests/c.at,v
| retrieving revision 1.1
| diff -p -u -r1.1 c.at
| --- tests/c.at 27 Sep 2001 13:28:15 -0000 1.1
| +++ tests/c.at 17 Apr 2002 21:47:55 -0000
| @@ -113,7 +113,7 @@ AT_SETUP([AC_PROG_CPP with warnings])
| AT_DATA([mycpp],
| [[#! /bin/sh
| echo noise >&2
| -exec ${1+"$@"}
| +exec "$@"
| ]])
|
| chmod +x mycpp
| @@ -146,7 +146,7 @@ AT_CHECK([/lib/cpp </dev/null || exit 77
| # A cpp which exit status is meaningless.
| AT_DATA([mycpp],
| [[#! /bin/sh
| -/lib/cpp ${1+"$@"}
| +/lib/cpp "$@"
| exit 0
| ]])
|
ok.
| Index: tests/wrappl.as
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/tests/wrappl.as,v
| retrieving revision 1.3
| diff -p -u -r1.3 wrappl.as
| --- tests/wrappl.as 12 Apr 2002 10:10:10 -0000 1.3
| +++ tests/wrappl.as 17 Apr 2002 21:47:55 -0000
| @@ -9,7 +9,10 @@ export autom4te_perllibdir
| case $as_me in
| ifnames)
| # Does not have lib files.
| - exec @abs_top_builddir@/bin/$as_me ${1+"$@"}
| + case $# in
| + 0) exec @abs_top_builddir@/bin/$as_me;;
| + *) exec @abs_top_builddir@/bin/$as_me "$@";;
| + esac
| ;;
|
| autom4te)
| @@ -19,6 +22,11 @@ case $as_me in
|
| esac
| # We might need files from build (frozen files), in addition of src files.
| -exec @abs_top_builddir@/bin/$as_me \
| +case $# in
| +0) exec @abs_top_builddir@/bin/$as_me \
| -I @abs_top_builddir@/lib \
| - -I @abs_top_srcdir@/lib ${1+"$@"}
| + -I @abs_top_srcdir@/lib;;
| +*) exec @abs_top_builddir@/bin/$as_me \
| + -I @abs_top_builddir@/lib \
| + -I @abs_top_srcdir@/lib "$@";;
| +esac
:( I really prefer the other approach.
| Index: tests/wrapsh.as
| ===================================================================
| RCS file: /cvsroot/autoconf/autoconf/tests/wrapsh.as,v
| retrieving revision 1.2
| diff -p -u -r1.2 wrapsh.as
| --- tests/wrapsh.as 12 Apr 2002 09:56:11 -0000 1.2
| +++ tests/wrapsh.as 17 Apr 2002 21:47:55 -0000
| @@ -1,4 +1,7 @@
| AS_INIT[]dnl -*- shell-script -*-
| # @configure_input@
| # Running `$0' as if it were installed.
| -exec @abs_top_builddir@/bin/$as_me --include @abs_top_builddir@/lib ${1+"$@"}
| +case $# in
| +0) exec @abs_top_builddir@/bin/$as_me --include @abs_top_builddir@/lib;;
| +*) exec @abs_top_builddir@/bin/$as_me --include @abs_top_builddir@/lib "$@";;
| +esac
|