[Top][All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: how to do "$@"?
From: |
Paul Eggert |
Subject: |
Re: how to do "$@"? |
Date: |
Wed, 17 Apr 2002 14:51:42 -0700 (PDT) |
> 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. 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.
2002-04-17 Paul Eggert <address@hidden>
Stop using "$@" when there might be zero positional arguments
in cases where this matters, as it's not portable to Zsh and
Mac OS X uses Zsh as /bin/sh.
* bin/autoconf.as: Ensure at least one positional arg before
using "$@", in cases where it's important to preserve argument count.
Do not use ${1+"$@"}, to avoid Zsh compatibility problem.
* bin/autoconf.in: Likewise.
* bin/autoheader.in: Likewise.
* bin/auto4mte.in: Likewise.
* bin/autoreconf.in: Likewise.
* bin/autoscan.in: Likewise.
* bin/autoupdate.in: Likewise.
* bin/ifnames.in: Likewise.
* config/missing: Likewise.
* lib/autoconf/programs.m4: Likewise.
* lib/autoconf/status.m4: Likewise.
* lib/autotest/general.m4: Likewise.
* lib/m4sugar/m4sh.m4: Likewise.
* tests/atgeneral.m4: Likewise.
* tests/autoreconf.in: Likewise.
* tests/c.at: Likewise.
* tests/wrappl.as: Likewise.
* tests/wrapsh.as: Likewise.
* bin/autoconf.in: Remove Zsh workaround; no longer needed.
* lib/m4sugar.m4sh.m4: Likewise.
* doc/autoconf.texi (Shell Substitutions): Document the problem.
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 "$@"
Index: bin/autoconf.in
===================================================================
RCS file: /cvsroot/autoconf/autoconf/bin/autoconf.in,v
retrieving revision 1.146
diff -p -u -r1.146 autoconf.in
--- bin/autoconf.in 10 Apr 2002 17:32:35 -0000 1.146
+++ bin/autoconf.in 17 Apr 2002 21:47:49 -0000
@@ -7,9 +7,6 @@
if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then
emulate sh
NULLCMD=:
- # Zsh performs word splitting on ${1+"$@"}, which is contrary to
- # our usage. Disable this feature.
- alias -g '${1+"$@"}'='"$@"'
elif test -n "${BASH_VERSION+set}" && (set -o posix) >/dev/null 2>&1; then
set -o posix
fi
@@ -281,7 +278,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 "$@"
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
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
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.
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],
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
;;
-*);;
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
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
@@ -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
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
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
]])
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
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
- Re: how to do "$@"?,
Paul Eggert <=