fixed a bug with expanding unquoted $* when the separator is not whitespace and one or more of the positional parameters ends with the separator, resulting in an extra blank field being generated; fixed an issue with adjusting SHLVL in subshells that caused the environment to be rebuilt too many times; fix to make sure function substitution doesn't leave the -v option unset

This commit is contained in:
Chet Ramey
2026-05-20 10:18:32 -04:00
parent 669b32f676
commit 2d4ba0c618
15 changed files with 503 additions and 36 deletions
+61
View File
@@ -12877,3 +12877,64 @@ lib/sh/stringvec.c,lib/sh/stringlist.c
of list_append/list_length, since they're always called with
WORD_LIST * arguments
5/8
---
command.h
- PF_STRINGEND,PF_STRINGBEG: new flags for the parameter expansion
functions; set if there are no characters following or preceding the
expansion, respectively
subst.c
- string_list_dollar_atstar: new function, expands unquoted $* and $@
when word splitting will take place and the separator is not
whitespace, and the positional parameters can potentially contain
the separator. It splits the positional parameters before separating
them with the separator, taking care to handle null positional
parameters and proper merging with text immediately preceding or
following the expansion. Fixes bug that added a spurious empty
argument if one of the positional parameters ended with the
separator
- string_list_internal: take a new flag value that says to add an
additional separator at the end of the returned string if the
last word is a null word and it will be joined with text following
the expansion
- string_list_pos_params: call string_list_dollar_atstar as
appropriate
- list_string: take a new word flag: W_SPLITONLY. This means not to
perform quoted null character removal while splitting the word
and don't allow CTLESC to quote anything, and to return "" results
of splitting verbatim, but marked as W_QUOTED to preserve null
arguments
- expand_word_internal,param_expand: set PF_STRINGEND and PF_STRINGBEG
appropriately
- word_split: pass W_SPLITONLY through to list_string()
These changes fix the bug with adding an extra empty word if one of
the positional parameters ends with the first character of $IFS and
it's not whitespace. Changes tests/dollar-at-star6.sub output.
arrayfunc.c
- array_value_internal: if we have an unquoted array expansion
subscripted with * or @, call string_list_dollar_atstar if
appropriate
5/13
----
execute_cmd.c
- execute_disk_command: if we update SHLVL, do it after (maybe)
making the export environment and then update the export env in
place, so we don't have to rebuild it twice or rebuild it more
times than necessary
Report and fix from Gao Xiang <gaoxiang@kylinos.cn>
5/14
----
variables.c
- adjust_shell_level,set_pwd: make sure variable binding works before
trying to set the variable as exported
Report and patch from Grisha Levit <grishalevit@gmail.com>
subst.c
- function_substitute: make sure funsubs don't leave `-v' unset
after they return by adding an unwind-protect function
uw_restore_verbose()
Report and patch from Grisha Levit <grishalevit@gmail.com>
+2
View File
@@ -1135,6 +1135,8 @@ tests/dollar-at-star8.sub f
tests/dollar-at-star9.sub f
tests/dollar-at-star10.sub f
tests/dollar-at-star11.sub f
tests/dollar-at-star12.sub f
tests/dollar-at-star13.sub f
tests/dollar-at1.sub f
tests/dollar-at2.sub f
tests/dollar-at3.sub f
+4
View File
@@ -1581,6 +1581,10 @@ array_value_internal (const char *s, int quoted, int flags, array_eltstate_t *es
retval = quote_nosplit (temp);
free (temp);
}
else if (quoted == 0 && (flags & AV_ASSIGNRHS) == 0 &&
ifs_is_set && ifs_is_null == 0 &&
spctabnl (ifs_firstc[0]) == 0)
retval = string_list_dollar_atstar (l, quoted, 0);
else /* ${name[@]} or unquoted ${name[*]} */
retval = string_list_dollar_at (l, quoted, (flags & AV_ASSIGNRHS) ? PF_ASSIGNRHS : 0);
+2
View File
@@ -116,6 +116,8 @@ enum command_type { cm_for, cm_case, cm_while, cm_if, cm_simple, cm_select,
#define PF_ALLINDS 0x40 /* array, act as if [@] was supplied */
#define PF_BACKQUOTE 0x80 /* differentiate `` from $() for command_substitute */
#define PF_COMSUBNLS 0x100 /* for ${; ...; } and read_comsub() to not strip trailing newlines */
#define PF_STRINGEND 0x200 /* set if there are no chars following this expansion */
#define PF_STRINGBEG 0x400 /* set if there are no chars before this expansion */
/* Possible values for subshell_environment */
#define SUBSHELL_ASYNC 0x01 /* subshell caused by `command &' */
+1 -1
View File
@@ -103,7 +103,7 @@ INC = -I. -I.. -I$(topdir) -I$(topdir)/lib -I$(topdir)/builtins -I${srcdir} \
ALLPROG = print truefalse sleep finfo logname basename dirname fdflags \
tty pathchk tee head mkdir rmdir mkfifo mktemp printenv id whoami \
uname sync push ln unlink realpath strftime mypid setpgid seq rm \
accept csv dsv cut stat getconf kv strptime chmod fltexpr jobid
accept csv dsv cut stat getconf kv strptime chmod fltexpr jobid rev
OTHERPROG = necho hello cat pushd asort
SUBDIRS = perl
+9 -3
View File
@@ -364,7 +364,7 @@ cutline (SHELL_VAR *v, arrayind_t ind, char *line, struct cutop *ops)
static int
cutfile (SHELL_VAR *v, WORD_LIST *list, struct cutop *ops)
{
int fd, unbuffered_read, r;
int fd, unbuffered_read, r, closefd;
char *line, *b;
size_t llen;
WORD_LIST *l;
@@ -378,11 +378,16 @@ cutfile (SHELL_VAR *v, WORD_LIST *list, struct cutop *ops)
l = list;
do
{
closefd = 0;
/* for each file */
if (l == 0 || (l->word->word[0] == '-' && l->word->word[1] == '\0'))
fd = 0;
else
fd = open (l->word->word, O_RDONLY);
{
fd = open (l->word->word, O_RDONLY);
closefd = fd != -1;
}
if (fd < 0)
{
file_error (l->word->word);
@@ -403,7 +408,8 @@ cutfile (SHELL_VAR *v, WORD_LIST *list, struct cutop *ops)
r = cutline (v, ind, line, ops); /* can modify line */
ind += r;
}
if (fd > 0)
if (closefd)
close (fd);
QUIT;
+22 -6
View File
@@ -5937,17 +5937,26 @@ execute_disk_command (WORD_LIST *words, REDIRECT *redirects, char *command_line,
if (command)
{
/* If we're optimizing out the fork (implicit `exec'), decrement the
shell level like `exec' would do. Don't do this if we are already
in a pipeline environment, assuming it's already been done. */
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE && (subshell_environment & SUBSHELL_PIPE) == 0)
adjust_shell_level (-1);
#if defined (STRICT_POSIX)
if (posixly_correct == 0 || subst_assign_varlist == 0) /* Done below. */
#endif
{
maybe_make_export_env ();
/* If we're optimizing out the fork (implicit `exec'), decrement the
shell level like `exec' would do. Don't do this if we are already
in a pipeline environment, assuming it's already been done.
Since we've just (possibly) remade the export environment, it's
marked as not dirty, so we update SHLVL in place rather than
remake the whole thing again or force a remake we would not
otherwise have to do. This mostly hits command substitutions with
a lot of exported variables, since command_substitute already
calls maybe_make_export_env (). */
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE && (subshell_environment & SUBSHELL_PIPE) == 0)
{
adjust_shell_level (-1);
update_export_env_inplace ("SHLVL=", 6, get_string_value ("SHLVL"));
array_needs_making = 0;
}
put_command_name_into_env (command);
}
}
@@ -6035,6 +6044,13 @@ execute_disk_command (WORD_LIST *words, REDIRECT *redirects, char *command_line,
expand_assignment_statements (command, 0);
maybe_make_export_env ();
/* See above for why we do this here. */
if (nofork && pipe_in == NO_PIPE && pipe_out == NO_PIPE && (subshell_environment & SUBSHELL_PIPE) == 0)
{
adjust_shell_level (-1);
update_export_env_inplace ("SHLVL=", 6, get_string_value ("SHLVL"));
array_needs_making = 0;
}
put_command_name_into_env (command);
}
#endif
+191 -10
View File
@@ -2875,6 +2875,9 @@ string_list_internal (WORD_LIST *list, char *sep, int flags)
result_size += strlen (t->word->word);
}
if (flags & 1)
result_size += sep_len;
r = result = (char *)xmalloc (result_size + 1);
for (t = list; t; t = t->next)
@@ -2893,6 +2896,20 @@ string_list_internal (WORD_LIST *list, char *sep, int flags)
word_len = strlen (t->word->word);
FASTCOPY (t->word->word, r, word_len);
r += word_len;
/* FLAGS & 1 means to add an additional SEP to the end of the returned
string if and only if the last word is a null word, since it will be
joined with text following the expansion. */
if ((flags & 1) && t->next == 0 && (t->word->word == 0 || t->word->word[0] == '\0') && (t->word->flags & W_QUOTED))
{
if (sep_len > 1)
{
FASTCOPY (sep, r, sep_len);
r += sep_len;
}
else if (sep_len)
*r++ = sep[0];
}
}
*r = '\0';
@@ -3071,6 +3088,107 @@ string_list_dollar_at (WORD_LIST *list, int quoted, int flags)
return ret;
}
/* Expand $* or $@ in a context where word splitting will be performed. We
separate the words in LIST with the first character of IFS and assume
that a later split will recreate the list.
To avoid issues with a non-whitespace separator producing spurious empty
words if the last character of one of the words in LIST is that same
separator, we split LIST and join each resultant word with SEP. We remove
quoted nulls from the result because the callers expect them not to
be present before another word splitting pass.
This does not use FLAGS yet, but could. */
char *
string_list_dollar_atstar (WORD_LIST *list, int quoted, int flags)
{
char *ret;
WORD_LIST *l, *l2, *tl;
#if defined (HANDLE_MULTIBYTE)
# if defined (__GNUC__)
char sep[MB_CUR_MAX + 1];
# else
char *sep = 0;
# endif
#else
char sep[2];
#endif
if (list == 0)
return (NULL);
#if defined (HANDLE_MULTIBYTE)
# if !defined (__GNUC__)
sep = (char *)xmalloc (locale_mb_cur_max + 1);
# endif /* !__GNUC__ */
if (ifs_firstc_len == 1)
{
sep[0] = ifs_firstc[0];
sep[1] = '\0';
}
else
{
memcpy (sep, ifs_firstc, ifs_firstc_len);
sep[ifs_firstc_len] = '\0';
}
#else
sep[0] = ifs_firstc;
sep[1] = '\0';
#endif
/* We want to split non-empty arguments, but preserve null arguments.
Preserving the null positional parameters and following them with SEP
is backwards compatible. */
/* If we want to remove (unquoted) null arguments, so $* is like $1 $2 ...,
then use PF_STRINGBEG and PF_STRINGEND to inhibit separators after the
first and last arguments so they are joined to whatever comes before
and after the $* and simply skip over them otherwise. */
/* Pre-process the list */
/* We arrange for null arguments to be preserved. */
for (l2 = tl = NULL, l = list; l; l = l->next)
{
char *new_string;
WORD_DESC *new_word;
new_word = copy_word (l->word);
new_word->flags |= W_SPLITONLY;
if (l->word->word[0] == '\0')
new_word->flags |= W_SAWQUOTEDNULL;
if (l2 == NULL)
l2 = tl = make_word_list (new_word, l2);
else
{
tl->next = make_word_list (new_word, (WORD_LIST *)NULL);
tl = tl->next;
}
}
/* XXX - this turns words that look like quoted nulls into "", which we
don't want here. */
l = word_list_split (l2); /* pre-split, preserving empty arguments */
/* We want to turn words that are QUOTED_NULL with W_HASQUOTEDNULL set in
the word flags back into "" but leave every other $'\177' alone. */
for (l2 = l; l2; l2 = l2->next)
if (QUOTED_NULL (l2->word->word) && (l2->word->flags & W_HASQUOTEDNULL))
{
l2->word->word[0] = '\0';
l2->word->flags &= ~W_HASQUOTEDNULL;
}
list_quote_escapes (l);
ret = string_list_internal (l, sep, (flags & PF_STRINGEND) ? 1 : 0);
#if defined (HANDLE_MULTIBYTE) && !defined (__GNUC__)
free (sep);
#endif
dispose_words (l2);
dispose_words (l);
return ret;
}
/* Turn the positional parameters into a string, understanding quoting and
the various subtleties of using the first character of $IFS as the
separator. Calls string_list_dollar_at, string_list_dollar_star, and
@@ -3100,6 +3218,14 @@ string_list_pos_params (int pchar, WORD_LIST *list, int quoted, int pflags)
ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0); /* Posix interp 888 */
else if (pchar == '*' && quoted == 0 && (pflags & PF_ASSIGNRHS)) /* XXX */
ret = expand_no_split_dollar_star ? string_list_dollar_star (list, quoted, 0) : string_list_dollar_at (list, quoted, 0); /* Posix interp 888 */
else if (pchar == '*' && quoted == 0 && (pflags & PF_ASSIGNRHS) == 0 &&
ifs_is_set && ifs_is_null == 0 &&
#if defined (HANDLE_MULTIBYTE)
spctabnl (ifs_firstc[0]) == 0)
#else
spctabnl (ifs_firstc) == 0)
#endif
ret = string_list_dollar_atstar (list, quoted, 0);
else if (pchar == '*')
{
/* Even when unquoted, string_list_dollar_star does the right thing
@@ -3123,14 +3249,16 @@ string_list_pos_params (int pchar, WORD_LIST *list, int quoted, int pflags)
that quotes the escapes. We could use string_list_internal with " "
as the second argument. */
ret = string_list_dollar_at (list, quoted, pflags); /* Posix interp 888 */
else if (pchar == '@')
#if 0
/* XXX - param_expand uses string_list_dollar_at() for this case. */
/* string_list_dollar_at quotes CTLESC, even if quoted == 0 */
ret = string_list_dollar_at (list, quoted, 0);
else if (pchar == '@' && quoted == 0 && (pflags & PF_ASSIGNRHS) == 0 &&
ifs_is_set && ifs_is_null == 0 &&
#if defined (HANDLE_MULTIBYTE)
spctabnl (ifs_firstc[0]) == 0) /* separate these cases for now */
#else
ret = string_list_dollar_star (list, quoted, 0);
spctabnl (ifs_firstc) == 0) /* separate these cases for now */
#endif
ret = string_list_dollar_atstar (list, quoted, 0);
else if (pchar == '@')
ret = string_list_dollar_star (list, quoted, 0);
else
ret = string_list ((quoted & (Q_HERE_DOCUMENT|Q_DOUBLE_QUOTES)) ? quote_list (list) : list);
@@ -3202,6 +3330,9 @@ list_string (char *string, char *separators, int flags)
else if (*s == CTLNUL) xflags |= SX_NOESCCTLNUL;
}
if (flags & W_SPLITONLY)
xflags |= SX_NOCTLESC|SX_NOESCCTLNUL;
slen = 0;
/* Remove sequences of whitespace at the beginning of STRING, as
long as those characters appear in IFS. Do not do this if
@@ -3242,7 +3373,7 @@ list_string (char *string, char *separators, int flags)
want to preserve the quoted null character iff this is a quoted
empty string; otherwise the quoted null characters are removed
below. */
if (QUOTED_NULL (current_word))
if ((flags & W_SPLITONLY) == 0 && QUOTED_NULL (current_word))
{
t = alloc_word_desc ();
t->word = make_quoted_char ('\0');
@@ -3253,7 +3384,8 @@ list_string (char *string, char *separators, int flags)
{
/* If we have something, then add it regardless. However,
perform quoted null character removal on the current word. */
remove_quoted_nulls (current_word);
if ((flags & W_SPLITONLY) == 0)
remove_quoted_nulls (current_word);
/* We don't want to set the word flags based on the string contents
here -- that's mostly for the parser -- so we just allocate a
@@ -3271,6 +3403,15 @@ list_string (char *string, char *separators, int flags)
if (current_word == 0 || current_word[0] == '\0')
result->word->flags |= W_SAWQUOTEDNULL;
}
else if ((flags & W_SPLITONLY) && current_word[0] == '\0')
{
t = alloc_word_desc ();
t->word = current_word;
/* W_QUOTED for string_list_internal () */
t->flags |= W_QUOTED|W_HASQUOTEDNULL;
result = make_word_list (t, result);
free_word = 0;
}
/* If we're not doing sequences of separators in the traditional
Bourne shell style, then add a quoted null argument. */
@@ -7008,6 +7149,13 @@ uw_restore_errexit (void *eflag)
set_shellopts ();
}
static void
uw_restore_verbose (void *vflag)
{
change_flag ('v', (intptr_t) vflag ? FLAG_ON : FLAG_OFF);
set_shellopts ();
}
/* Quote the output of nofork varsub command substitution in the way that the
caller of function_substitute expects. The caller guarantees that STRING
is non-null. This is equivalent to what read_comsub does to the output it
@@ -7146,7 +7294,8 @@ function_substitute (char *string, int quoted, int flags)
push_context (lambdafunc.name, 1, temporary_env); /* make local variables work */
this_shell_function = &lambdafunc;
unwind_protect_int (verbose_flag);
add_unwind_protect (uw_restore_verbose, (void *) (intptr_t) verbose_flag);
change_flag ('v', FLAG_OFF);
/* When inherit_errexit option is not enabled, command substitution does
@@ -10821,6 +10970,20 @@ param_expand (char *string, size_t *sindex, int quoted,
# endif
/* Posix interp 888: not RHS, no splitting, IFS set to '' */
temp = string_list_dollar_star (list, quoted, 0);
else if (expand_no_split_dollar_star == 0 && (pflags & PF_ASSIGNRHS) == 0 &&
ifs_is_set && ifs_is_null == 0 &&
# if defined (HANDLE_MULTIBYTE)
spctabnl (ifs_firstc[0]) == 0)
# else
spctabnl (ifs_firstc) == 0)
# endif
{
int nflags;
/* XXX - only if $# > 1? */
nflags = (string[zindex+1] == '\0') ? PF_STRINGEND : 0;
temp = string_list_dollar_atstar (list, quoted, nflags);
}
else
{
temp = string_list_dollar_at (list, quoted, 0);
@@ -10927,6 +11090,20 @@ param_expand (char *string, size_t *sindex, int quoted,
else
temp = string_list_dollar_at (list, quoted, pflags);
}
else if (quoted == 0 &&
ifs_is_set && ifs_is_null == 0 &&
#if defined (HANDLE_MULTIBYTE)
spctabnl (ifs_firstc[0]) == 0)
#else
spctabnl (ifs_firstc) == 0)
#endif
{
int nflags;
/* XXX - only if $# > 1? */
nflags = (string[zindex+1] == '\0') ? PF_STRINGEND : 0;
temp = string_list_dollar_atstar (list, quoted, pflags|nflags);
}
else
temp = string_list_dollar_at (list, quoted, pflags);
@@ -11520,6 +11697,8 @@ add_string:
if (temp)
{
istring = sub_append_string (temp, istring, &istring_index, &istring_size);
if (istring_index > 0)
pflags &= ~PF_STRINGBEG;
temp = (char *)0;
}
@@ -11694,6 +11873,8 @@ add_string:
pflags |= PF_ASSIGNRHS;
if (word->flags & W_COMPLETE)
pflags |= PF_COMPLETE;
if (istring_index == 0)
pflags |= PF_STRINGBEG;
tword = param_expand (string, &sindex, quoted, &local_expanded,
&temp_has_dollar_at, &quoted_dollar_at,
@@ -12554,7 +12735,7 @@ word_split (WORD_DESC *w, char *ifs_chars)
char *xifs;
xifs = ((w->flags & W_QUOTED) || ifs_chars == 0) ? "" : ifs_chars;
result = list_string (w->word, xifs, w->flags & W_QUOTED);
result = list_string (w->word, xifs, w->flags & (W_QUOTED|W_SPLITONLY));
}
else
result = (WORD_LIST *)NULL;
+5
View File
@@ -130,6 +130,11 @@ extern char *string_list_dollar_star (WORD_LIST *, int, int);
/* Expand $@ into a single string, obeying POSIX rules. */
extern char *string_list_dollar_at (WORD_LIST *, int, int);
/* A special function for expanding unquoted $* and $@ in a context where
word splitting will be performed using a non-whitespace separator and
the positional parameters potentially contain that separator. */
extern char *string_list_dollar_atstar (WORD_LIST *, int, int);
/* Turn the positional parameters into a string, understanding quoting and
the various subtleties of using the first character of $IFS as the
separator. Calls string_list_dollar_at, string_list_dollar_star, and
+5
View File
@@ -266,6 +266,11 @@ test_runsub ./dollar-at-star9.sub
test_runsub ./dollar-at-star10.sub
test_runsub ./dollar-at-star11.sub
# tests for unquoted expansions of $* and $@ when word splitting will take
# place and the separator is not whitespace
test_runsub ./dollar-at-star12.sub
test_runsub ./dollar-at-star13.sub
# tests for special expansion of "$*" and "${array[*]}" when used with other
# expansions -- bugs through bash-2.05b
test_runsub ./dollar-star1.sub
+60
View File
@@ -0,0 +1,60 @@
OIFS=$IFS
set -- a b+ c +d e; IFS=+;
# these should be the same
printf '<%s> ' $1 $2 $3 $4 $5; echo
printf '<%s> ' $* ; echo
printf '<%s> ' $@ ; echo
set -- 'b+' 'c'
# these should be the same
echo =====
printf '<%s> ' $1 $2 $3; echo
printf '<%s> ' $* ; echo
printf '<%s> ' $@ ; echo
printf '<%s> ' ${*} ; echo
printf '<%s> ' ${@} ; echo
# these should be the same
echo =====
set -- a 'b+c' '+d+e' 'f+g+' h
printf '<%s> ' $* ; echo
printf '<%s> ' $@ ; echo
echo =====
array=(a 'b+' c '+d' e)
printf '<%s> ' ${array[0]} ${array[1]} ${array[2]} ${array[3]} ${array[4]}; echo
printf '<%s> ' ${array[*]} ; echo
printf '<%s> ' ${array[@]} ; echo
unset array
array=(a 'b+c' '+d+e' 'f+g+' h)
printf '<%s> ' ${array[0]} ${array[1]} ${array[2]} ${array[3]} ${array[4]}; echo
printf '<%s> ' ${array[*]} ; echo
printf '<%s> ' ${array[@]} ; echo
unset array
echo =====
set -- $'\001\177+' 'b+' c; IFS=+;
recho $1 $2 $3
recho $*
set -- $'\177+' 'b+' c; IFS=+;
recho $1 $2 $3
recho $*
set -- $'a\177+' 'b+' c; IFS=+;
recho $1 $2 $3
recho $*
set -- $'\001+' b+ c; IFS=+;
recho $1 $2 $3
recho $*
set -- $'a\001+' b+ c; IFS=+;
recho $1 $2 $3
recho $*
+41
View File
@@ -0,0 +1,41 @@
# from back in 2018 on dash@vger.kernel.org
# IFS the default, these arguments should not be split
set -- , ,
IFS=,
recho $@
recho $*
recho ${@}
recho ${*}
set -- $@
recho $#
# IFS already set, these arguments should be split
set -- , ,
recho $@
recho $*
recho ${@}
recho ${*}
set -- $@
recho $#
# need the space to be preserved
set -- , ' ,'
IFS=,
recho $@
recho $*
set -- $@
recho $#
# need the , to preserve an empty argument before the trailing char
# from back in 2018 on dash@vger.kernel.org
set -- ',' ','
recho ${@}a
recho ${*}b
recho a${@}
recho b${*}
+91 -10
View File
@@ -247,22 +247,18 @@ argv[3] = <'c'>
argv[1] = <>
argv[2] = <a>
argv[3] = <>
argv[4] = <>
argv[5] = <b>
argv[6] = <>
argv[7] = <>
argv[8] = <c>
argv[4] = <b>
argv[5] = <>
argv[6] = <c>
argv[1] = <'a'>
argv[2] = <'b'>
argv[3] = <'c'>
argv[1] = <>
argv[2] = <a>
argv[3] = <>
argv[4] = <>
argv[5] = <b>
argv[6] = <>
argv[7] = <>
argv[8] = <c>
argv[4] = <b>
argv[5] = <>
argv[6] = <c>
argv[1] = <'a'>
argv[2] = <'b'>
argv[3] = <'c'>
@@ -477,6 +473,91 @@ argv[1] = <>
argv[1] = <>
argv[1] = </>
1:1
dollar-at-star12.sub
<a> <b> <c> <> <d> <e>
<a> <b> <c> <> <d> <e>
<a> <b> <c> <> <d> <e>
=====
<b> <c>
<b> <c>
<b> <c>
<b> <c>
<b> <c>
=====
<a> <b> <c> <> <d> <e> <f> <g> <h>
<a> <b> <c> <> <d> <e> <f> <g> <h>
=====
<a> <b> <c> <> <d> <e>
<a> <b> <c> <> <d> <e>
<a> <b> <c> <> <d> <e>
<a> <b> <c> <> <d> <e> <f> <g> <h>
<a> <b> <c> <> <d> <e> <f> <g> <h>
<a> <b> <c> <> <d> <e> <f> <g> <h>
=====
argv[1] = <^A^?>
argv[2] = <b>
argv[3] = <c>
argv[1] = <^A^?>
argv[2] = <b>
argv[3] = <c>
argv[1] = <^?>
argv[2] = <b>
argv[3] = <c>
argv[1] = <^?>
argv[2] = <b>
argv[3] = <c>
argv[1] = <a^?>
argv[2] = <b>
argv[3] = <c>
argv[1] = <a^?>
argv[2] = <b>
argv[3] = <c>
argv[1] = <^A>
argv[2] = <b>
argv[3] = <c>
argv[1] = <^A>
argv[2] = <b>
argv[3] = <c>
argv[1] = <a^A>
argv[2] = <b>
argv[3] = <c>
argv[1] = <a^A>
argv[2] = <b>
argv[3] = <c>
dollar-at-star13.sub
argv[1] = <>
argv[2] = <>
argv[1] = <>
argv[2] = <>
argv[1] = <>
argv[2] = <>
argv[1] = <>
argv[2] = <>
argv[1] = <2>
argv[1] = <>
argv[2] = <>
argv[1] = <>
argv[2] = <>
argv[1] = <>
argv[2] = <>
argv[1] = <>
argv[2] = <>
argv[1] = <2>
argv[1] = <>
argv[2] = < >
argv[1] = <>
argv[2] = < >
argv[1] = <2>
argv[1] = <>
argv[2] = <>
argv[3] = <a>
argv[1] = <>
argv[2] = <>
argv[3] = <b>
argv[1] = <a>
argv[2] = <>
argv[1] = <b>
argv[2] = <>
dollar-star1.sub
xa|xb|xc
xa|xb|xc
+1 -1
View File
@@ -1,4 +1,4 @@
unset GROUPS UID 2>/dev/null
unset GROUPS 2>/dev/null
${THIS_SH} ./test.tests >${BASH_TSTOUT} 2>&1
diff ${BASH_TSTOUT} test.right && rm -f ${BASH_TSTOUT}
+8 -5
View File
@@ -888,8 +888,8 @@ adjust_shell_level (int change)
new_level[3] = '\0';
}
temp_var = bind_variable ("SHLVL", new_level, 0);
set_auto_export (temp_var);
if (temp_var = bind_variable ("SHLVL", new_level, 0))
set_auto_export (temp_var);
}
static void
@@ -929,7 +929,8 @@ set_pwd (void)
if (posixly_correct && current_dir)
{
temp_var = bind_variable ("PWD", current_dir, 0);
set_auto_export (temp_var);
if (temp_var)
set_auto_export (temp_var);
}
free (current_dir);
}
@@ -938,7 +939,8 @@ set_pwd (void)
{
set_working_directory (home_string);
temp_var = bind_variable ("PWD", home_string, 0);
set_auto_export (temp_var);
if (temp_var)
set_auto_export (temp_var);
}
else
{
@@ -946,7 +948,8 @@ set_pwd (void)
if (temp_string)
{
temp_var = bind_variable ("PWD", temp_string, 0);
set_auto_export (temp_var);
if (temp_var)
set_auto_export (temp_var);
free (temp_string);
}
}