- 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>
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
.\" 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
.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
.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
.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
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})
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.
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.
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)''.
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
#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);
{
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 ();
}
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 ();
}
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;
((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
/* 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;
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:
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
test_runsub ./redir12.sub
${THIS_SH} < ./redir13.in
+
+test_runsub ./redir14.sub
--- /dev/null
+: ${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 )'
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 ();
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;