change posix-mode implicit redirection from /dev/null for asynchronous commands so that 0<&0 does not count as an explicit redirection for austin-group interp 1913; fix spurious debug message about job notification for -c command; fix error handling for wait builtin if there are no children in a subshell

This commit is contained in:
Chet Ramey
2026-01-06 10:46:14 -05:00
parent a43e08df2d
commit e7c3acf108
12 changed files with 174 additions and 28 deletions
+30
View File
@@ -12482,3 +12482,33 @@ redir.c
- stdin_redirection: now take the entire REDIRECT * as its argument,
so we can do additional posix-mandated checking on redirections
like 0<&0, to satisfy interp 1913
- stdin_redirection: if we are in posix mode, a redirection like
0<&0 does not count as an explicit stdin redirection for the purpose
of redirecting input from /dev/null, POSIX interp 1913
doc/bash.1,doc/bashref.texi
- update the description of implicit redirection of stdin for async
commands from /dev/null with the caveat that an explicit redirection
of stdin overrides this
- update the posix mode section with info from interp 1913 about how
<&0 doesn't count as an explicit redirection of stdin
1/2/2026
--------
jobs.c
- notify_of_job_status: hold onto the job's exit status without printing
a notification if the shell has started to run -c command and the
job is in a () subshell or a compound command with pipe input;
otherwise we get a spurious catch-all message
1/5
---
variables.c
- initialize_shell_variables: change string index and length variables
to size_t
Inspired by report from Marc Aurèle La France <tsi@tuyoix.net>
jobs.c
- wait_for_background_pids: treat wait_for_single_pid returning > 256
as an error, same as returning < 0, and check errno appropriately
Report from Aleksey Covacevice <aleksey.covacevice@gmail.com>
+1
View File
@@ -1492,6 +1492,7 @@ tests/redir10.sub f
tests/redir11.sub f
tests/redir12.sub f
tests/redir13.in f
tests/redir14.sub f
tests/rhs-exp.tests f
tests/rhs-exp.right f
tests/rhs-exp1.sub f
+13 -5
View File
@@ -5,7 +5,7 @@
.\" Case Western Reserve University
.\" chet.ramey@case.edu
.\"
.\" Last Change: Fri Dec 26 18:21:22 EST 2025
.\" Last Change: Wed Dec 31 18:30:12 EST 2025
.\"
.\" For bash_builtins, strip all but "SHELL BUILTIN COMMANDS" section
.\" For rbash, strip all but "RESTRICTED SHELL" section
@@ -22,7 +22,7 @@
.ds zX \" empty
.if \n(zZ=1 .ig zZ
.if \n(zY=1 .ig zY
.TH BASH 1 "2025 December 26" "GNU Bash 5.3"
.TH BASH 1 "2025 December 31" "GNU Bash 5.3"
.\"
.ie \n(.g \{\
.ds ' \(aq
@@ -774,11 +774,18 @@ of a semicolon to delimit commands.
.PP
If a command is terminated by the control operator
.BR & ,
the shell executes the command in the \fIbackground\fP
the shell executes the command asynchronously
in a subshell.
This is known as executing a command in the \fIbackground\fP,
and these are referred to as \fIasynchronous\fP commands.
The shell does not wait for the command to
finish, and the return status is 0.
These are referred to as \fIasynchronous\fP commands.
When job control is not active,
the standard input for asynchronous commands,
in the absence of any explicit redirections involving the standard input,
is redirected from
.FN /dev/null .
.PP
Commands separated or terminated by
.B ;
(or an equivalent
@@ -5920,7 +5927,8 @@ for how to control this behavior when not in posix mode.
.PP
If a command is followed by a \fB&\fP and job control is not active, the
default standard input for the command is the empty file
.FN /dev/null .
.FN /dev/null ,
unless the command has an explicit redirection involving the standard input.
Otherwise, the invoked command inherits the file descriptors of the calling
shell as modified by redirections.
.SH ENVIRONMENT
+17 -4
View File
@@ -914,8 +914,9 @@ and these are referred to as @dfn{asynchronous} commands.
The shell does not wait for the command to finish, and the return
status is 0 (true).
When job control is not active (@pxref{Job Control}),
the standard input for asynchronous commands, in the absence of any
explicit redirections, is redirected from @code{/dev/null}.
the standard input for asynchronous commands,
in the absence of any explicit redirections involving the standard input,
is redirected from @file{/dev/null}.
Commands separated or terminated by
@samp{;} (or equivalent @code{newline})
@@ -3867,7 +3868,8 @@ See the description of the @code{inherit_errexit} shell option
in @sc{posix} mode.
If a command is followed by a @samp{&} and job control is not active, the
default standard input for the command is the empty file @file{/dev/null}.
default standard input for the command is the empty file @file{/dev/null},
unless the command has an explicit redirection involving the standard input.
Otherwise, the invoked command inherits the file descriptors of the calling
shell as modified by redirections.
@@ -9240,7 +9242,8 @@ The Bash @dfn{posix mode} changes the Bash
behavior in these areas so that it conforms more strictly
to the standard.
Starting Bash with the @option{--posix} command-line option or executing
Starting Bash with the @option{--posix} or @option{-o posix}
command-line option or executing
@samp{set -o posix} while Bash is running will cause Bash to conform more
closely to the @sc{posix} standard by changing the behavior to
match that specified by @sc{posix} in areas where the Bash default differs.
@@ -9364,6 +9367,16 @@ Bash will not insert a command without the execute bit set into the
command hash table, even if it returns it as a (last-ditch) result
from a @env{$PATH} search.
@item
Normally, when job control is not enabled,
the shell implicitly redirects the standard input of
asynchronous commands from @file{/dev/null}.
A redirection to the standard input in this command inhibits this
implicit redirection.
In @sc{posix} mode, a redirection that redirects file descriptor 0
to itself (e.g., @samp{<&0}) does not count as a redirection that
overrides the implicit redirection from @file{/dev/null}.
@item
The message printed by the job control code and builtins when a job
exits with a non-zero status is ``Done(status)''.
+2 -3
View File
@@ -2,11 +2,10 @@
Copyright (C) 1988-2025 Free Software Foundation, Inc.
@end ignore
@set LASTCHANGE Fri Dec 26 18:21:22 EST 2025
@set LASTCHANGE Wed Dec 31 18:26:37 EST 2025
@set EDITION 5.3
@set VERSION 5.3
@set UPDATED 26 December 2025
@set UPDATED 31 December 2025
@set UPDATED-MONTH December 2025
+10 -11
View File
@@ -847,7 +847,8 @@ execute_command_internal (COMMAND *command, int asynchronous, int pipe_in, int p
#endif /* COMMAND_TIMING */
/* Is this a compound command with a redirection from stdin? POSIX interp
1913 makes it matter. */
1913 makes it matter. There is an exception for 0<&0 or <&0 or equivalent
when in posix mode. */
if (shell_control_structure (command->type) && command->redirects)
{
stdin_redirected = stdin_redirects (command->redirects);
@@ -4876,13 +4877,11 @@ run_builtin:
{
if ((cmdflags & CMD_STDIN_REDIR) &&
pipe_in == NO_PIPE &&
#if 0 /*TAG:bash-5.4 POSIX interp 1913 */
/* POSIX interp 1913 says that the redirection of fd 0
from /dev/null is unconditional. */
(posixly_correct || stdin_redirects (simple_command->redirects) == 0))
#else
from /dev/null is performed unless the command has
a redirection that's something like 0<&0 or <&0.
See redir.c:stdin_redirection() for the details. */
(stdin_redirects (simple_command->redirects) == 0))
#endif
async_redirect_stdin ();
setup_async_signals ();
}
@@ -5917,15 +5916,15 @@ execute_disk_command (WORD_LIST *words, REDIRECT *redirects, char *command_line,
in asynchronous children. */
if (async)
{
/*itrace("execute_disk_command: async = 1 cmd_stdin_redir = %d stdin_redirects (redirects) = %d",
(cmdflags & CMD_STDIN_REDIR), stdin_redirects (redirects));*/
if ((cmdflags & CMD_STDIN_REDIR) &&
pipe_in == NO_PIPE &&
#if 0 /*TAG:bash-5.4 POSIX interp 1913 */
/* POSIX interp 1913 says that the redirection of fd 0
from /dev/null is unconditional. */
(posixly_correct || stdin_redirects (redirects) == 0))
#else
from /dev/null is performed unless the command has
a redirection that's something like 0<&0 or <&0.
See redir.c:stdin_redirection() for the details. */
(stdin_redirects (redirects) == 0))
#endif
async_redirect_stdin ();
setup_async_signals ();
}
+10 -2
View File
@@ -2841,7 +2841,7 @@ wait_for_background_pids (int wflags, struct procstat *ps)
ps->pid = pid;
ps->status = (r < 0 || r > 256) ? 127 : r;
}
if (r == -1 && errno == ECHILD)
if ((r < 0 || r > 256) && errno == ECHILD)
{
/* If we're mistaken about job state, compensate. */
check_async = 0;
@@ -4603,6 +4603,14 @@ notify_of_job_status (int wanted)
((DEADJOB (job) && IS_FOREGROUND (job) == 0) || STOPPED (job)))
continue;
/* hang onto the status if the shell is running -c command and the
command is running in a () subshell or a compound command with
pipe input */
else if (startup_state == 2 && (subshell_environment & (SUBSHELL_PAREN|SUBSHELL_PIPE)) &&
WIFSIGNALED (s) == 0 &&
((DEADJOB (job) && IS_FOREGROUND (job) == 0) || STOPPED (job)))
continue;
/* If job control is disabled, don't print the status messages.
Mark dead jobs as notified so that they get cleaned up. If
startup_state == 2 and subshell_environment has the
@@ -4684,7 +4692,7 @@ notify_of_job_status (int wanted)
/* XXX - this is a catch-all in case we missed a state */
else
{
internal_debug("notify_of_job_status: catch-all setting J_NOTIFIED on job %d (%d), startup state = %d", job, jobs[job]->flags, startup_state);
internal_debug("notify_of_job_status: catch-all setting J_NOTIFIED on job %d (%d), startup state = %d subshell_environment = %d", job, jobs[job]->flags, startup_state, subshell_environment);
jobs[job]->flags |= J_NOTIFIED;
}
break;
+8 -1
View File
@@ -1400,8 +1400,15 @@ stdin_redirection (REDIRECT *rp)
case r_reading_string:
return (1);
case r_duplicating_input:
case r_duplicating_input_word:
case r_close_this:
return (rp->redirector.dest == 0
#if 1 /*TAG: bash-5.4 POSIX interp 1913 */
&& (posixly_correct == 0 || rp->redirectee.dest != 0)
#endif
);
case r_duplicating_input_word:
/* we defer evaluation of this until later, so just return based on the
destination for now. */
return (rp->redirector.dest == 0);
case r_output_direction:
case r_appending_to:
+31
View File
@@ -195,3 +195,34 @@ after ERR trap: 1
got error ERR
./redir12.sub: line 56: unreadable-file: Permission denied
/tmp
redir14.sub
standard 1 - <&0
line 1
line 2
posix 1 - <&0
standard 2 - 0<&0
line 1
line 2
posix 2 - 0<&0
standard 3 - subshell containing AND-OR list <&0
line 1
line 2
posix 3 - subshell containing AND-OR list <&0
standard - subshell with explicit redirection
line 1
line 2
posix - subshell with explicit redirection
line 1
line 2
standard 1 - compound command with AND-OR list with pipe input
hello inpipe
posix 1 - compound command with AND-OR list with pipe input
hello inpipe
standard 2 - simple command with AND-OR list with pipe input
jello inpipe
posix 2 - simple command with AND-OR list with pipe input
jello inpipe
standard 3 - subshell command with AND-OR list with pipe input
hello inpipe
posix 3 - subshell command with AND-OR list with pipe input
hello inpipe
+2
View File
@@ -224,3 +224,5 @@ test_runsub ./redir11.sub
test_runsub ./redir12.sub
${THIS_SH} < ./redir13.in
test_runsub ./redir14.sub
+47
View File
@@ -0,0 +1,47 @@
: ${TMPDIR:=/tmp} ${THIS_SH:=./bash}
TMPFILE=$TMPDIR/redir-in-$$
trap 'rm -f $TMPFILE' 0
cat >$TMPFILE <<EOF
line 1
line 2
EOF
export TMPFILE
echo standard 1 - '<&0'
${THIS_SH} -c 'exec < $TMPFILE ; cat <&0 & wait'
echo posix 1 - '<&0'
${THIS_SH} -o posix -c 'exec < $TMPFILE ; cat <&0 & wait'
echo standard 2 - '0<&0'
${THIS_SH} -c 'exec < $TMPFILE ; cat 0<&0 & wait'
echo posix 2 - '0<&0'
${THIS_SH} -o posix -c 'exec < $TMPFILE ; cat 0<&0 & wait'
echo standard 3 - subshell containing AND-OR list '<&0'
${THIS_SH} -c 'exec < $TMPFILE ; ( cat <&0 & wait )'
echo posix 3 - subshell containing AND-OR list '<&0'
${THIS_SH} -o posix -c 'exec < $TMPFILE ; ( cat <&0 & wait )'
echo standard - subshell with explicit redirection
${THIS_SH} -c '( cat <&0 & wait ) <$TMPFILE'
echo posix - subshell with explicit redirection
${THIS_SH} -o posix -c '( cat <&0 & wait ) <$TMPFILE'
# pipeline input inhibits the implicit redirection from /dev/null; posix mode
# does not make a difference -- POSIX interp 1913
echo standard 1 - compound command with AND-OR list with pipe input
${THIS_SH} -c 'echo "hello inpipe" | { cat 0<&0 & wait; }'
echo posix 1 - compound command with AND-OR list with pipe input
${THIS_SH} -o posix -c 'echo "hello inpipe" | { cat 0<&0 & wait; }'
echo standard 2 - simple command with AND-OR list with pipe input
${THIS_SH} -c 'echo "jello inpipe" | cat 0<&0 & wait'
echo posix 2 - simple command with AND-OR list with pipe input
${THIS_SH} -o posix -c 'echo "jello inpipe" | cat 0<&0 & wait'
echo standard 3 - subshell command with AND-OR list with pipe input
${THIS_SH} -c 'echo "hello inpipe" | ( cat <&0 & wait )'
echo posix 3 - subshell command with AND-OR list with pipe input
${THIS_SH} -o posix -c 'echo "hello inpipe" | ( cat <&0 & wait )'
+3 -2
View File
@@ -369,7 +369,8 @@ void
initialize_shell_variables (char **env, int privmode)
{
char *name, *string, *temp_string;
int c, char_index, string_index, string_length, ro;
int c, ro;
size_t char_index, string_index, string_length;
SHELL_VAR *temp_var;
create_variable_tables ();
@@ -454,7 +455,7 @@ initialize_shell_variables (char **env, int privmode)
STREQN (BASHARRAY_SUFFIX, name + char_index - BASHARRAY_SUFFLEN, BASHARRAY_SUFFLEN) &&
*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')')
{
size_t namelen;
size_t namelen, slen;
char *tname; /* desired imported array variable name */
namelen = char_index - BASHARRAY_PREFLEN - BASHARRAY_SUFFLEN;