From: Chet Ramey Date: Fri, 5 Dec 2025 20:50:38 +0000 (-0500) Subject: implementation of printf '%N$' numbered argument conversion specifier, compatible... X-Git-Url: http://git.ipfire.org/gitweb/index.cgi?a=commitdiff_plain;h=f27bf94a7927219f96e97e1a21f2ade9dd439a3e;p=thirdparty%2Fbash.git implementation of printf '%N$' numbered argument conversion specifier, compatible with coreutils --- diff --git a/CWRU/CWRU.chlog b/CWRU/CWRU.chlog index 65f4c95b..ab59fcd1 100644 --- a/CWRU/CWRU.chlog +++ b/CWRU/CWRU.chlog @@ -12311,3 +12311,19 @@ execute_cmd.c - execute_in_subshell: clear the exit trap before checking for a pending fatal signal From https://savannah.gnu.org/bugs/?67745 + + 12/2 + ---- +builtins/printf.def + - import %N$ specifier implementation from printf-nspecifier branch; + implementation is similar to coreutils'. + - we warn about mixing numbered and unnumbered format specifiers in + posix mode + - getint: now takes second argument to deal with numbered specifiers + - getstar: new function to get precision and fieldwidth from argument + list if `*' in format specifier, handling numbered arguments + - narg, nptr: we build a new format string as necessary + Originally from https://savannah.gnu.org/support/?111166, also + discussed in thread starting with + https://lists.gnu.org/archive/html/bug-bash/2025-02/msg00151.html + diff --git a/MANIFEST b/MANIFEST index d1b00de7..f9af056b 100644 --- a/MANIFEST +++ b/MANIFEST @@ -1442,6 +1442,7 @@ tests/printf4.sub f tests/printf5.sub f tests/printf6.sub f tests/printf7.sub f +tests/printf8.sub f tests/procsub.tests f tests/procsub.right f tests/procsub1.sub f diff --git a/builtins/printf.def b/builtins/printf.def index 2bb70581..b3622017 100644 --- a/builtins/printf.def +++ b/builtins/printf.def @@ -100,6 +100,8 @@ extern int errno; { \ QUIT; \ retval = value; \ + if (narg_argc != -1) \ + free (narg_argv); \ if (conv_bufsize > 4096 ) \ { \ free (conv_buf); \ @@ -178,6 +180,10 @@ extern int errno; #define LENMODS "hjlLtz" #define DIGITS "0123456789" +#ifndef NL_ARGMAX +# define NL_ARGMAX 999 +#endif + #ifndef TIMELEN_MAX # define TIMELEN_MAX 128 #endif @@ -202,7 +208,8 @@ static int vbprintf (const char *, ...) __attribute__((__format__ (printf, 1, 2) static char *mklong (char *, char *, size_t); static int getchr (void); static char *getstr (void); -static int getint (int); +static int getint (int, int); +static int getstar (char **, int); static intmax_t getintmax (void); static uintmax_t getuintmax (void); @@ -247,12 +254,42 @@ static size_t vblen; static char **narg_argv; static int narg_argc; static int narg_maxind; +static int narg_numind; /* last numbered argument specification */ +static int narg_seqind; /* only used when mixing numbered and unnumbered conversions */ +static int narg_base; /* used on format reuse */ +static char *narg_arg; /* argument corresponding to the numbered conversion spec */ +static int narg_convtype; /* 1 = numbered, 0 = unnumbered */ +static int narg_convwarned; /* want to minimize the warnings */ +static char *nfmt = 0; static intmax_t tw; static char *conv_buf; static size_t conv_bufsize; +static void +init_numarg () +{ + size_t len; + WORD_LIST *l; + + len = list_length ((GENERIC_LIST *)orig_arglist); + narg_argv = (char **)xmalloc ((len + 2) * sizeof (char *)); /* +2 because we don't use 0 */ + + for (narg_argc = 1, l = orig_arglist; l; l = l->next) + narg_argv[narg_argc++] = l->word->word; + + /* If we've processed some unnumbered conversion specifications before + we get the first numbered one, count those as "previous conversion + specifications that consumed an argument." */ + for (narg_seqind = 0, l = orig_arglist; l != garglist; l = l->next) + narg_seqind++; + + narg_argv[narg_argc] = NULL; + narg_maxind = narg_numind = narg_base = 0; + narg_arg = NULL; +} + static inline int decodeint (char **str, int diagnose, int overflow_return) { @@ -281,7 +318,10 @@ printf_builtin (WORD_LIST *list) { int ch, fieldwidth, precision; int have_fieldwidth, have_precision, use_Lmod, altform, longform; + int moreargs; char convch, thisch, nextch, *format, *modstart, *precstart, *fmt, *start; + char *nptr, *origfmt; + size_t nargind; #if defined (HANDLE_MULTIBYTE) char mbch[25]; /* 25 > MB_LEN_MAX, plus can handle 4-byte UTF-8 and large Unicode characters*/ int mbind, mblen, mb_cur_max; @@ -293,6 +333,8 @@ printf_builtin (WORD_LIST *list) conversion_error = 0; vflag = 0; + narg_argc = -1; + reset_internal_getopt (); while ((ch = internal_getopt (list, "v:")) != -1) { @@ -357,15 +399,23 @@ printf_builtin (WORD_LIST *list) mb_cur_max = MB_CUR_MAX; + nfmt = xrealloc (nfmt, strlen (format) + 1); /* XXX error checking */ + nfmt[0] = '\0'; + /* Basic algorithm is to scan the format string for conversion specifications -- once one is found, find out if the field width or precision is a '*'; if it is, gather up value. Note, format strings are reused as necessary to use up the provided arguments, arguments of zero/null string are provided to use up the format string. */ + + /* We only warn once, even if we reuse the format, and we only warn in + posix mode. */ + narg_convwarned = !posixly_correct; do { tw = 0; + /* find next format specification */ for (fmt = format; *fmt; fmt++) { @@ -409,7 +459,9 @@ printf_builtin (WORD_LIST *list) } /* ASSERT(*fmt == '%') */ - start = fmt++; + origfmt = fmt; /* saved for format errors */ + nptr = start = nfmt; /* we construct our own format string */ + *nptr++ = *fmt++; if (*fmt == '%') /* %% prints a % */ { @@ -417,37 +469,117 @@ printf_builtin (WORD_LIST *list) continue; } + narg_convtype = 0; + /* Look for possible %N$ numbered conversion specifier. */ + /* "Conversions can be applied to the nth argument operand rather + than to the next argument operand. In this case, the conversion + specifier character '%' is replaced by the sequence "%n$", + where n is a decimal integer in the range [1,{NL_ARGMAX}], + giving the argument operand number. This feature provides for + the definition of format strings that select arguments in an + order appropriate to specific languages." */ + /* This leaves nargv_curind pointing to the argument corresponding + to the numbered conversion spec or the next sequential one after + having processed a numbered spec, so the advancearg() in the + various code handling the conversion specifiers advances it. */ + /* We don't allow N$ for precision or field width at this time. */ + nargind = strspn (fmt, DIGITS); + if (nargind > 0 && fmt[nargind] == '$') + { + char *ep; + int narg, thisarg; + + if (garglist != orig_arglist && narg_convwarned == 0) + { + builtin_warning ("%s", _("should not mix numbered and unnumbered conversions")); + narg_convwarned = 1; + } + if (narg_argc == -1) + init_numarg (); + thisarg = (int)strtol (fmt, &ep, 10); + /* "If it is a numbered argument conversion specification, + printf should write a diagnostic message to standard error + and exit with non-zero status" */ + if (thisarg <= 0 || thisarg >= narg_argc) + { + /* We don't want to print this error message for numbered + conversions exceeding the number of arguments unless + we are in posix mode, so we set narg_numind = narg_argc + if we are not. */ + if (thisarg <= 0 || posixly_correct) + { + builtin_error (_("%d: numbered conversion out of range"), thisarg); + PRETURN (EXECUTION_FAILURE); + } + thisarg = narg_argc; + } + thisarg += narg_base; + narg_numind = (thisarg < narg_argc) ? thisarg : narg_argc; + if (narg_numind > narg_maxind) + narg_maxind = narg_numind; + narg_arg = narg_argv[narg_numind]; + narg_convtype = 1; + fmt += nargind + 1; + } + else if (narg_argc != -1) + { + int thisarg; + + if (narg_convwarned == 0) + { + builtin_warning ("%s", _("should not mix numbered and unnumbered conversions")); + narg_convwarned = 1; + } + /* There is genuine incompatibility here between macOS/FreeBSD + printf and coreutils printf. + Given '%s %3$s %s\n' A B C D, coreutils printf treats the + unnumbered coversion specs sequentially, so it echoes "A C B", + then "D" on a new line. + FreeBSD printf treats the next unnumbered specifier following + a numbered specifier as numbered + 1, and prints "A C D". + zsh and ksh93 are like coreutils; mksh is like FreeBSD. + We follow coreutils here for now, but subject to change. */ + narg_arg = (narg_seqind < narg_argc) ? narg_argv[++narg_seqind] : NULL; + if (narg_seqind > narg_maxind) + narg_maxind = narg_seqind; + } + /* Found format specification, skip to field width. We check for alternate form for possible later use. */ - for (; *fmt && strchr(SKIP1, *fmt); ++fmt) - if (*fmt == '#') - altform++; + while (*fmt && strchr(SKIP1, *fmt)) + { + if (*fmt == '#') + altform++; + *nptr++ = *fmt++; /* build format string */ + } + *nptr = '\0'; /* Skip optional field width. */ if (*fmt == '*') { - fmt++; + *nptr++ = *fmt++; have_fieldwidth = 1; /* Handle field with overflow by ignoring fieldwidth for now. - getint() prints a message. */ - fieldwidth = getint (0); + getstar() prints a message. */ + fieldwidth = getstar (&fmt, 0); } else while (DIGIT (*fmt)) - fmt++; + *nptr++ = *fmt++; + *nptr = '\0'; /* Skip optional '.' and precision */ if (*fmt == '.') { - ++fmt; + *nptr++ = *fmt++; if (*fmt == '*') { - fmt++; + *nptr++ = *fmt++; have_precision = 1; /* Handle precision overflow by ignoring precision for now. - getint() prints a message. + getstar() prints a message. "A negative precision is treated as if it were missing." */ - precision = getint (-1); + precision = getstar (&fmt, -1); } else { @@ -460,12 +592,13 @@ printf_builtin (WORD_LIST *list) #else if (*fmt == '-') #endif - fmt++; + *nptr++ = *fmt++; if (DIGIT (*fmt)) precstart = fmt; while (DIGIT (*fmt)) - fmt++; + *nptr++ = *fmt++; } + *nptr = '\0'; } /* skip possible format modifiers */ @@ -475,7 +608,7 @@ printf_builtin (WORD_LIST *list) { use_Lmod |= USE_LONG_DOUBLE && *fmt == 'L'; longform |= *fmt == 'l'; - fmt++; + *nptr++ = *fmt++; } if (*fmt == 0) @@ -484,6 +617,9 @@ printf_builtin (WORD_LIST *list) PRETURN (EXECUTION_FAILURE); } + *nptr++ = *fmt; + *nptr = '\0'; + convch = *fmt; thisch = modstart[0]; nextch = modstart[1]; @@ -607,7 +743,7 @@ printf_builtin (WORD_LIST *list) if (*fmt != ')' || *++fmt != 'T') { builtin_warning (_("`%c': invalid time format specification"), *fmt); - fmt = start; + fmt = origfmt; free (timefmt); PC (*fmt); continue; @@ -620,7 +756,10 @@ printf_builtin (WORD_LIST *list) } /* argument is seconds since the epoch with special -1 and -2 */ /* default argument is equivalent to -1; special case */ - arg = garglist ? getintmax () : -1; + if (narg_argc != -1) + arg = (narg_numind < narg_argc && narg_argv[narg_numind]) ? getintmax (): -1; + else + arg = garglist ? getintmax () : -1; if (arg == -1) secs = NOW; /* roughly date +%s */ else if (arg == -2) @@ -643,8 +782,8 @@ printf_builtin (WORD_LIST *list) else timebuf[sizeof(timebuf) - 1] = '\0'; /* convert to %s format that preserves fieldwidth and precision */ - modstart[0] = 's'; - modstart[1] = '\0'; + *nptr++ = 's'; + *nptr = '\0'; r = printstr (start, timebuf, strlen (timebuf), fieldwidth, precision); /* XXX - %s for now */ if (r < 0) PRETURN (EXECUTION_FAILURE); @@ -821,8 +960,44 @@ printf_builtin (WORD_LIST *list) /* PRETURN will print error message. */ PRETURN (EXECUTION_FAILURE); } + + /* "The format operand shall be reused as often as necessary to satisfy + the argument operands. If conversion specifications beginning with + a "%n$" sequence are used, on format reuse the value of n shall + refer to the nth argument operand following the highest numbered + argument operand consumed by the previous use of the format operand." */ + if (narg_argc != -1) + { + /* If we consumed the last argument, we're done. */ + moreargs = (narg_numind < narg_argc) && (narg_maxind < narg_argc) && (narg_seqind < narg_argc); + narg_base = narg_maxind; + narg_maxind = 0; /* need to recalculate this */ + } + else + moreargs = garglist && garglist != list->next; + + /* "on format reuse the value of n shall refer to the nth argument + operand following the highest numbered argument operand consumed + by the previous use of the format operand." */ + /* This mess is to handle combining numbered and unnumbered conversion + specifiers. */ + if (moreargs && narg_argc != -1) + { + /* If we decide to treat numbered and unnumbered specifiers with + different counters. */ + if (garglist == 0 && orig_arglist != 0) + moreargs = 0; + /* I don't like this -- POSIX says "previous conversion specification + that consumed an argument", not "highest-numbered argument + processed" -- but this is what coreutils printf seems to do. */ + narg_seqind = narg_base; + /* The second clause will be true if we processed the last + argument (not necessarily all arguments). */ + if (narg_seqind >= narg_argc || (narg_base + 1) >= narg_argc) + moreargs = 0; + } } - while (garglist && garglist != list->next); + while (moreargs); if (conversion_error) retval = EXECUTION_FAILURE; @@ -1346,13 +1521,25 @@ mklong (char *str, char *modifiers, size_t mlen) static inline char * getarg (void) { + if (narg_argc != -1) + return narg_arg; + return (garglist ? garglist->word->word : 0); } static inline void advancearg (void) { - garglist = garglist->next; + if (narg_argc != -1) + { +#if 0 + /* see if we need to manage narg_seqind here or in printf_builtin */ + if (narg_numind < narg_argc) + narg_numind++; +#endif + } + else + garglist = garglist->next; } static int @@ -1404,15 +1591,19 @@ chk_converror (char *s, char *ep) } /* Don't call getintmax here because it may consume an argument on error, and - we call this to get field width/precision arguments. */ + we call this to get field width/precision arguments. This is only called + by getstar() to get field width/precision values from arguments. It does + not call getarg() and it does not advance the argument with advancearg. */ static int -getint (int overflow_retval) +getint (int numberedconv, int overflow_retval) { intmax_t ret; char *ep, *arg; int overflow; - arg = getarg (); + /* XXX - check here that narg_numind < narg_argc and return null in that case? */ + arg = (narg_argc != -1) ? (numberedconv ? narg_argv[narg_numind] : narg_argv[narg_seqind]) + : (garglist ? garglist->word->word : 0); if (arg == 0) return (0); @@ -1427,10 +1618,63 @@ getint (int overflow_retval) chk_converror (arg, ep); - advancearg (); return (overflow ? overflow_retval : (int)ret); } +static int +getstar (char **fmtp, int overflow_retval) +{ + int ret, numconv; + char *ep, **fp; + size_t l, ind; + + fp = fmtp; + + numconv = 0; + ep = *fp; + l = DIGIT (**fp) ? strspn (ep, DIGITS) : 0; + if (l > 0 && ep[l] == '$') + { + if (narg_argc == -1) + return overflow_retval; + ind = decodeint (fp, 1, -1); + if (**fp == '$') + { + ep = *fp + 1; + fp = &ep; + } + *fmtp = *fp; + ind += narg_base; + if (ind > 0 && ind <= narg_argc) /* can't have 0-based indices */ + narg_numind = ind; + else + return overflow_retval; + numconv = 1; + if (narg_numind > narg_maxind) + narg_maxind = narg_numind; + } + else if (narg_argc != -1) + { + if (narg_convwarned == 0) + { + builtin_warning ("%s", _("should not mix numbered and unnumbered conversions")); + narg_convwarned = 1; + } + /* We manage the sequential index here and in printf_builtin */ + if (narg_seqind < narg_argc) + ++narg_seqind; + if (narg_seqind > narg_maxind) + narg_maxind = narg_seqind; + } + + ret = getint (numconv, overflow_retval); + + if (narg_argc == -1) + advancearg (); + + return ret; +} + static intmax_t getintmax (void) { diff --git a/doc/bash.0 b/doc/bash.0 index da4d7d8f..1549badb 100644 --- a/doc/bash.0 +++ b/doc/bash.0 @@ -1689,6 +1689,9 @@ PPAARRAAMMEETTEERRSS The "+=" operator appends to an array variable when assigning using the compound assignment syntax; see PPAARRAAMMEETTEERRSS above. + If one of the word expansions in a compound array assignment unsets the + variable, the results are unspecified. + An array element is referenced using ${_n_a_m_e[_s_u_b_s_c_r_i_p_t]}. The braces are required to avoid conflicts with pathname expansion. If _s_u_b_s_c_r_i_p_t is @@ or **, the word expands to all members of _n_a_m_e, unless noted in the @@ -1899,7 +1902,7 @@ EEXXPPAANNSSIIOONN _p_a_r_a_m_e_t_e_r is a positional parameter with more than one digit, or when _p_a_r_a_m_e_t_e_r is followed by a character which is not to be interpreted as part of its name. The _p_a_r_a_m_e_t_e_r is a shell parameter as described - above PPAARRAAMMEETTEERRSS) or an array reference (AArrrraayyss). + above (PPAARRAAMMEETTEERRSS) or an array reference (AArrrraayyss). If the first character of _p_a_r_a_m_e_t_e_r is an exclamation point (!!), and _p_a_r_a_m_e_t_e_r is not a _n_a_m_e_r_e_f, it introduces a level of indirection. BBaasshh @@ -3729,6 +3732,7 @@ RREEAADDLLIINNEE form sseett _v_a_r_i_a_b_l_e_-_n_a_m_e _v_a_l_u_e + or using the bbiinndd builtin command (see SSHHEELLLL BBUUIILLTTIINN CCOOMMMMAANNDDSS below). Except where noted, rreeaaddlliinnee variables can take the values OOnn or OOffff @@ -4829,7 +4833,7 @@ RREEAADDLLIINNEE fined, programmable completion performs rreeaaddlliinnee's default completion. The options supplied to ccoommpplleettee and ccoommppoopptt can control how rreeaaddlliinnee - treats the completions. For instance, the _-_o _f_u_l_l_q_u_o_t_e option tells + treats the completions. For instance, the --oo ffuullllqquuoottee option tells rreeaaddlliinnee to quote the matches as if they were filenames. See the de- scription of ccoommpplleettee below for details. @@ -5745,8 +5749,8 @@ SSHHEELLLL BBUUIILLTTIINN CCOOMMMMAANNDDSS directories in which to search for _f_i_l_e_n_a_m_e. The default for BBAASSHH__LLOOAADDAABBLLEESS__PPAATTHH is system-dependent, and may include "." to force a search of the current directory. The --dd option will - delete a builtin previously loaded with --ff. If _-_s is used with - _-_f, the new builtin becomes a POSIX special builtin. + delete a builtin previously loaded with --ff. If --ss is used with + --ff, the new builtin becomes a POSIX special builtin. If no options are supplied and a _n_a_m_e is not a shell builtin, eennaabbllee will attempt to load _n_a_m_e from a shared object named @@ -6164,7 +6168,7 @@ SSHHEELLLL BBUUIILLTTIINN CCOOMMMMAANNDDSS last. If the top element of the directory stack is modified, and the - _-_n option was not supplied, ppooppdd uses the ccdd builtin to change + --nn option was not supplied, ppooppdd uses the ccdd builtin to change to the directory at the top of the stack. If the ccdd fails, ppooppdd returns a non-zero value. @@ -6232,12 +6236,27 @@ SSHHEELLLL BBUUIILLTTIINN CCOOMMMMAANNDDSS is the numeric value of the following character, using the cur- rent locale. - The _f_o_r_m_a_t is reused as necessary to consume all of the _a_r_g_u_- + Format specifiers may apply to the _nth argument rather than the + next sequential argument. In this case, the '%' in the format + specifier is replaced by the sequence "%_n$", where _n is a deci- + mal integer greater than 0, giving the argument number to use as + the operand. The format string should not mix numbered and un- + numbered argument specifiers, though this is allowed. Unnum- + bered argument specifiers always refer to the next argument fol- + lowing the last argument consumed by an unnumbered specifier; + numbered argument specifiers refer to absolute positions in the + argument list. + + The _f_o_r_m_a_t is reused as necessary to consume all of the _a_r_g_u_- _m_e_n_t_s. If the _f_o_r_m_a_t requires more _a_r_g_u_m_e_n_t_s than are supplied, - the extra format specifications behave as if a zero value or - null string, as appropriate, had been supplied. The return - value is zero on success, non-zero if an invalid option is sup- - plied or a write or assignment error occurs. + the extra format specifications behave as if a zero value or + null string, as appropriate, had been supplied. If _f_o_r_m_a_t is + reused, a numbered argument specifier "%_n$" refers to the _nth + argument following the highest numbered argument consumed by the + previous use of _f_o_r_m_a_t. + + The return value is zero on success, non-zero if an invalid op- + tion is supplied or a write or assignment error occurs. ppuusshhdd [--nn] [+_n] [-_n] ppuusshhdd [--nn] [_d_i_r] @@ -7555,4 +7574,4 @@ BBUUGGSS Array variables may not (yet) be exported. -GNU Bash 5.3 2025 October 6 _B_A_S_H(1) +GNU Bash 5.3 2025 December 2 _B_A_S_H(1) diff --git a/doc/bash.1 b/doc/bash.1 index afe320ee..4554754e 100644 --- a/doc/bash.1 +++ b/doc/bash.1 @@ -5,7 +5,7 @@ .\" Case Western Reserve University .\" chet.ramey@case.edu .\" -.\" Last Change: Mon Nov 17 11:37:04 EST 2025 +.\" Last Change: Tue Dec 2 16:43:36 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 November 17" "GNU Bash 5.3" +.TH BASH 1 "2025 December 2" "GNU Bash 5.3" .\" .ie \n(.g \{\ .ds ' \(aq @@ -11148,10 +11148,28 @@ except that a leading plus or minus sign is allowed, and if the leading character is a single or double quote, the value is the numeric value of the following character, using the current locale. .IP +Format specifiers may apply to the \fIn\fPth argument rather than the next +sequential argument. +In this case, the '%' in the format specifier is replaced by the +sequence +.Q %\fIn\fP$ , +where \fIn\fP is a decimal integer greater than 0, +giving the argument number to use as the operand. +The format string should not mix numbered and unnumbered argument specifiers, +though this is allowed. +Unnumbered argument specifiers always refer to the next argument following +the last argument consumed by an unnumbered specifier; numbered argument +specifiers refer to absolute positions in the argument list. +.IP The \fIformat\fP is reused as necessary to consume all of the \fIarguments\fP. If the \fIformat\fP requires more \fIarguments\fP than are supplied, the extra format specifications behave as if a zero value or null string, as appropriate, had been supplied. +If \fIformat\fP is reused, a numbered argument specifier +.Q %\fIn\fP$ +refers to the \fIn\fPth argument following the highest numbered argument +consumed by the previous use of \fIformat\fP. +.IP The return value is zero on success, non-zero if an invalid option is supplied or a write or assignment error occurs. diff --git a/doc/bash.html b/doc/bash.html index 46a6da12..14e4e194 100644 --- a/doc/bash.html +++ b/doc/bash.html @@ -1,5 +1,5 @@ - + @@ -3736,6 +3736,10 @@ an index of −1 references the last element.

assigning using the compound assignment syntax; see PARAMETERS above.

+

If one of the +word expansions in a compound array assignment unsets the +variable, the results are unspecified.

+

An array element is referenced using ${name[subscript]}. The braces are required to avoid conflicts with pathname @@ -4056,7 +4060,7 @@ required when parameter is a positional parameter with more than one digit, or when parameter is followed by a character which is not to be interpreted as part of its name. The parameter is a shell parameter -as described above PARAMETERS) or an array reference +as described above (PARAMETERS) or an array reference (Arrays).

If the first @@ -7793,9 +7797,9 @@ with a statement of the form

set variable−name value

-

or using the bind builtin -command (see SHELL BUILTIN COMMANDS -below).

+

or using the +bind builtin command (see SHELL BUILTIN +COMMANDS below).

Except where noted, readline variables can take the values @@ -9875,7 +9879,7 @@ completion.

The options supplied to complete and compopt can control how readline treats the completions. For instance, -the −o fullquote option tells readline +the −o fullquote option tells readline to quote the matches as if they were filenames. See the description of complete below for details.

@@ -10537,9 +10541,10 @@ interpretation.

All builtins except :, true, false, echo, and -test/[ accept --help as a special -option. If --help is supplied, these builtins output -a help message and exit with a status of 0.
+test/[ accept −−help as a +special option. If −−help is supplied, +these builtins output a help message and exit with a status +of 0.
:
[arguments]

No effect; the command does @@ -12119,8 +12124,8 @@ The default for BASH_LOADABLES_PATH is system-dependent, and may include “.” to force a search of the current directory. The −d option will delete a builtin previously loaded with -−f. If −s is used with -−f, the new builtin becomes a +−f. If −s is used with +−f, the new builtin becomes a POSIX special builtin.

If no options @@ -13011,7 +13016,7 @@ directory, “popd −1” the next to last.

If the top element of the directory stack is modified, and the -−n option was not supplied, popd uses +−n option was not supplied, popd uses the cd builtin to change to the directory at the top of the stack. If the cd fails, popd returns a non-zero value.

@@ -13131,14 +13136,33 @@ the leading character is a single or double quote, the value is the numeric value of the following character, using the current locale.

+

Format +specifiers may apply to the nth argument rather than +the next sequential argument. In this case, the +’%’ in the format specifier is replaced by the +sequence “%n$”, where n is a +decimal integer greater than 0, giving the argument number +to use as the operand. The format string should not mix +numbered and unnumbered argument specifiers, though this is +allowed. Unnumbered argument specifiers always refer to the +next argument following the last argument consumed by an +unnumbered specifier; numbered argument specifiers refer to +absolute positions in the argument list.

+

The format is reused as necessary to consume all of the arguments. If the format requires more arguments than are supplied, the extra format specifications behave as if a zero value or null string, as -appropriate, had been supplied. The return value is zero on -success, non-zero if an invalid option is supplied or a -write or assignment error occurs.

+appropriate, had been supplied. If format is reused, +a numbered argument specifier “%n$” +refers to the nth argument following the highest +numbered argument consumed by the previous use of +format.

+ +

The return +value is zero on success, non-zero if an invalid option is +supplied or a write or assignment error occurs.

pushd [−n] [+n] [−n]
diff --git a/doc/bash.info b/doc/bash.info index 17059200..3143f1c2 100644 --- a/doc/bash.info +++ b/doc/bash.info @@ -1,9 +1,9 @@ This is bash.info, produced by makeinfo version 7.2 from bashref.texi. This text is a brief description of the features that are present in the -Bash shell (version 5.3, 24 September 2025). +Bash shell (version 5.3, 2 December 2025). - This is Edition 5.3, last updated 24 September 2025, of ‘The GNU Bash + This is Edition 5.3, last updated 2 December 2025, of ‘The GNU Bash Reference Manual’, for ‘Bash’, Version 5.3. Copyright © 1988-2025 Free Software Foundation, Inc. @@ -26,10 +26,10 @@ Bash Features ************* This text is a brief description of the features that are present in the -Bash shell (version 5.3, 24 September 2025). The Bash home page is +Bash shell (version 5.3, 2 December 2025). The Bash home page is . - This is Edition 5.3, last updated 24 September 2025, of ‘The GNU Bash + This is Edition 5.3, last updated 2 December 2025, of ‘The GNU Bash Reference Manual’, for ‘Bash’, Version 5.3. Bash contains features that appear in other popular shells, and some @@ -4369,7 +4369,7 @@ standard. The ‘-f’ option means to load the new builtin command NAME from shared object FILENAME, on systems that support dynamic loading. - If FILENAME does not contain a slash. Bash will use the value of + If FILENAME does not contain a slash, Bash will use the value of the ‘BASH_LOADABLES_PATH’ variable as a colon-separated list of directories in which to search for FILENAME. The default for ‘BASH_LOADABLES_PATH’ is system-dependent, and may include "." to @@ -4561,12 +4561,25 @@ standard. the numeric value of the following character, using the current locale. + Format specifiers may apply to the Nth argument rather than the + next sequential argument. In this case, the ‘%’ in the format + specifier is replaced by the sequence ‘%N$’, where N is a decimal + integer greater than 0, giving the argument number to use as the + operand. The format string should not mix numbered and unnumbered + argument specifiers, though this is allowed. Unnumbered argument + specifiers always refer to the next argument following the last + argument consumed by an unnumbered specifier; numbered argument + specifiers refer to absolute positions in the argument list. + The FORMAT is reused as necessary to consume all of the ARGUMENTS. If the FORMAT requires more ARGUMENTS than are supplied, the extra format specifications behave as if a zero value or null string, as - appropriate, had been supplied. The return value is zero on - success, non-zero if an invalid option is supplied or a write or - assignment error occurs. + appropriate, had been supplied. If FORMAT is reused, a numbered + argument specifier ‘%N$’ refers to the Nth argument following the + highest numbered argument consumed by the previous use of FORMAT. + + The return value is zero on success, non-zero if an invalid option + is supplied or a write or assignment error occurs. ‘read’ read [-Eers] [-a ANAME] [-d DELIM] [-i TEXT] [-n NCHARS] @@ -7310,6 +7323,9 @@ end of the array, and an index of -1 references the last element. The ‘+=’ operator appends to an array variable when assigning using the compound assignment syntax; see *note Shell Parameters:: above. + If one of the word expansions in a compound array assignment unsets +the variable, the results are unspecified. + An array element is referenced using ‘${NAME[SUBSCRIPT]}’. The braces are required to avoid conflicts with the shell's filename expansion operators. If the SUBSCRIPT is ‘@’ or ‘*’, the word expands @@ -7569,8 +7585,8 @@ can appear in the prompt variables ‘PS0’, ‘PS1’, ‘PS2’, and ‘PS4 ‘\\’ A backslash. ‘\[’ - Begin a sequence of non-printing characters. Thiss could be used - to embed a terminal control sequence into the prompt. + Begin a sequence of non-printing characters. This could be used to + embed a terminal control sequence into the prompt. ‘\]’ End a sequence of non-printing characters. @@ -12950,8 +12966,8 @@ D.1 Index of Shell Builtin Commands (line 71) * pwd: Bourne Shell Builtins. (line 265) -* read: Bash Builtins. (line 558) -* readarray: Bash Builtins. (line 669) +* read: Bash Builtins. (line 571) +* readarray: Bash Builtins. (line 682) * readonly: Bourne Shell Builtins. (line 277) * return: Bourne Shell Builtins. @@ -12960,7 +12976,7 @@ D.1 Index of Shell Builtin Commands * shift: Bourne Shell Builtins. (line 327) * shopt: The Shopt Builtin. (line 9) -* source: Bash Builtins. (line 678) +* source: Bash Builtins. (line 691) * suspend: Job Control Builtins. (line 141) * test: Bourne Shell Builtins. @@ -12971,12 +12987,12 @@ D.1 Index of Shell Builtin Commands (line 446) * true: Bourne Shell Builtins. (line 512) -* type: Bash Builtins. (line 683) -* typeset: Bash Builtins. (line 720) -* ulimit: Bash Builtins. (line 726) +* type: Bash Builtins. (line 696) +* typeset: Bash Builtins. (line 733) +* ulimit: Bash Builtins. (line 739) * umask: Bourne Shell Builtins. (line 517) -* unalias: Bash Builtins. (line 834) +* unalias: Bash Builtins. (line 847) * unset: Bourne Shell Builtins. (line 535) * wait: Job Control Builtins. @@ -13660,138 +13676,138 @@ D.5 Concept Index  Tag Table: -Node: Top903 -Node: Introduction2846 -Node: What is Bash?3059 -Node: What is a shell?4192 -Node: Definitions6802 -Node: Basic Shell Features10129 -Node: Shell Syntax11353 -Node: Shell Operation12380 -Node: Quoting13671 -Node: Escape Character15009 -Node: Single Quotes15544 -Node: Double Quotes15893 -Node: ANSI-C Quoting17238 -Node: Locale Translation18632 -Node: Creating Internationalized Scripts20035 -Node: Comments24233 -Node: Shell Commands25000 -Node: Reserved Words25939 -Node: Simple Commands27082 -Node: Pipelines27744 -Node: Lists31000 -Node: Compound Commands32920 -Node: Looping Constructs33929 -Node: Conditional Constructs36478 -Node: Command Grouping51615 -Node: Coprocesses53107 -Node: GNU Parallel55793 -Node: Shell Functions56711 -Node: Shell Parameters65159 -Node: Positional Parameters70060 -Node: Special Parameters71150 -Node: Shell Expansions74611 -Node: Brace Expansion76800 -Node: Tilde Expansion80136 -Node: Shell Parameter Expansion83091 -Node: Command Substitution103738 -Node: Arithmetic Expansion107267 -Node: Process Substitution108443 -Node: Word Splitting109551 -Node: Filename Expansion111995 -Node: Pattern Matching115219 -Node: Quote Removal120985 -Node: Redirections121289 -Node: Executing Commands131545 -Node: Simple Command Expansion132212 -Node: Command Search and Execution134320 -Node: Command Execution Environment136764 -Node: Environment140212 -Node: Exit Status142115 -Node: Signals144174 -Node: Shell Scripts149122 -Node: Shell Builtin Commands152420 -Node: Bourne Shell Builtins154761 -Node: Bash Builtins181480 -Node: Modifying Shell Behavior218404 -Node: The Set Builtin218746 -Node: The Shopt Builtin230740 -Node: Special Builtins247793 -Node: Shell Variables248782 -Node: Bourne Shell Variables249216 -Node: Bash Variables251724 -Node: Bash Features291008 -Node: Invoking Bash292022 -Node: Bash Startup Files298606 -Node: Interactive Shells303848 -Node: What is an Interactive Shell?304256 -Node: Is this Shell Interactive?304918 -Node: Interactive Shell Behavior305742 -Node: Bash Conditional Expressions309503 -Node: Shell Arithmetic314920 -Node: Aliases318247 -Node: Arrays321381 -Node: The Directory Stack328969 -Node: Directory Stack Builtins329766 -Node: Controlling the Prompt334211 -Node: The Restricted Shell337096 -Node: Bash POSIX Mode339978 -Node: Shell Compatibility Mode358925 -Node: Job Control367932 -Node: Job Control Basics368389 -Node: Job Control Builtins374757 -Node: Job Control Variables381545 -Node: Command Line Editing382776 -Node: Introduction and Notation384479 -Node: Readline Interaction386831 -Node: Readline Bare Essentials388019 -Node: Readline Movement Commands389827 -Node: Readline Killing Commands390823 -Node: Readline Arguments392846 -Node: Searching393936 -Node: Readline Init File396179 -Node: Readline Init File Syntax397482 -Node: Conditional Init Constructs424433 -Node: Sample Init File428818 -Node: Bindable Readline Commands431938 -Node: Commands For Moving433476 -Node: Commands For History435940 -Node: Commands For Text441331 -Node: Commands For Killing445456 -Node: Numeric Arguments448244 -Node: Commands For Completion449396 -Node: Keyboard Macros455092 -Node: Miscellaneous Commands455793 -Node: Readline vi Mode462360 -Node: Programmable Completion463337 -Node: Programmable Completion Builtins473073 -Node: A Programmable Completion Example484810 -Node: Using History Interactively490155 -Node: Bash History Facilities490836 -Node: Bash History Builtins494571 -Node: History Interaction501042 -Node: Event Designators505992 -Node: Word Designators507570 -Node: Modifiers509962 -Node: Installing Bash511899 -Node: Basic Installation513015 -Node: Compilers and Options516891 -Node: Compiling For Multiple Architectures517641 -Node: Installation Names519394 -Node: Specifying the System Type521628 -Node: Sharing Defaults522374 -Node: Operation Controls523088 -Node: Optional Features524107 -Node: Reporting Bugs536830 -Node: Major Differences From The Bourne Shell538187 -Node: GNU Free Documentation License559614 -Node: Indexes584791 -Node: Builtin Index585242 -Node: Reserved Word Index592340 -Node: Variable Index594785 -Node: Function Index612198 -Node: Concept Index626193 +Node: Top899 +Node: Introduction2838 +Node: What is Bash?3051 +Node: What is a shell?4184 +Node: Definitions6794 +Node: Basic Shell Features10121 +Node: Shell Syntax11345 +Node: Shell Operation12372 +Node: Quoting13663 +Node: Escape Character15001 +Node: Single Quotes15536 +Node: Double Quotes15885 +Node: ANSI-C Quoting17230 +Node: Locale Translation18624 +Node: Creating Internationalized Scripts20027 +Node: Comments24225 +Node: Shell Commands24992 +Node: Reserved Words25931 +Node: Simple Commands27074 +Node: Pipelines27736 +Node: Lists30992 +Node: Compound Commands32912 +Node: Looping Constructs33921 +Node: Conditional Constructs36470 +Node: Command Grouping51607 +Node: Coprocesses53099 +Node: GNU Parallel55785 +Node: Shell Functions56703 +Node: Shell Parameters65151 +Node: Positional Parameters70052 +Node: Special Parameters71142 +Node: Shell Expansions74603 +Node: Brace Expansion76792 +Node: Tilde Expansion80128 +Node: Shell Parameter Expansion83083 +Node: Command Substitution103730 +Node: Arithmetic Expansion107259 +Node: Process Substitution108435 +Node: Word Splitting109543 +Node: Filename Expansion111987 +Node: Pattern Matching115211 +Node: Quote Removal120977 +Node: Redirections121281 +Node: Executing Commands131537 +Node: Simple Command Expansion132204 +Node: Command Search and Execution134312 +Node: Command Execution Environment136756 +Node: Environment140204 +Node: Exit Status142107 +Node: Signals144166 +Node: Shell Scripts149114 +Node: Shell Builtin Commands152412 +Node: Bourne Shell Builtins154753 +Node: Bash Builtins181472 +Node: Modifying Shell Behavior219208 +Node: The Set Builtin219550 +Node: The Shopt Builtin231544 +Node: Special Builtins248597 +Node: Shell Variables249586 +Node: Bourne Shell Variables250020 +Node: Bash Variables252528 +Node: Bash Features291812 +Node: Invoking Bash292826 +Node: Bash Startup Files299410 +Node: Interactive Shells304652 +Node: What is an Interactive Shell?305060 +Node: Is this Shell Interactive?305722 +Node: Interactive Shell Behavior306546 +Node: Bash Conditional Expressions310307 +Node: Shell Arithmetic315724 +Node: Aliases319051 +Node: Arrays322185 +Node: The Directory Stack329888 +Node: Directory Stack Builtins330685 +Node: Controlling the Prompt335130 +Node: The Restricted Shell338014 +Node: Bash POSIX Mode340896 +Node: Shell Compatibility Mode359843 +Node: Job Control368850 +Node: Job Control Basics369307 +Node: Job Control Builtins375675 +Node: Job Control Variables382463 +Node: Command Line Editing383694 +Node: Introduction and Notation385397 +Node: Readline Interaction387749 +Node: Readline Bare Essentials388937 +Node: Readline Movement Commands390745 +Node: Readline Killing Commands391741 +Node: Readline Arguments393764 +Node: Searching394854 +Node: Readline Init File397097 +Node: Readline Init File Syntax398400 +Node: Conditional Init Constructs425351 +Node: Sample Init File429736 +Node: Bindable Readline Commands432856 +Node: Commands For Moving434394 +Node: Commands For History436858 +Node: Commands For Text442249 +Node: Commands For Killing446374 +Node: Numeric Arguments449162 +Node: Commands For Completion450314 +Node: Keyboard Macros456010 +Node: Miscellaneous Commands456711 +Node: Readline vi Mode463278 +Node: Programmable Completion464255 +Node: Programmable Completion Builtins473991 +Node: A Programmable Completion Example485728 +Node: Using History Interactively491073 +Node: Bash History Facilities491754 +Node: Bash History Builtins495489 +Node: History Interaction501960 +Node: Event Designators506910 +Node: Word Designators508488 +Node: Modifiers510880 +Node: Installing Bash512817 +Node: Basic Installation513933 +Node: Compilers and Options517809 +Node: Compiling For Multiple Architectures518559 +Node: Installation Names520312 +Node: Specifying the System Type522546 +Node: Sharing Defaults523292 +Node: Operation Controls524006 +Node: Optional Features525025 +Node: Reporting Bugs537748 +Node: Major Differences From The Bourne Shell539105 +Node: GNU Free Documentation License560532 +Node: Indexes585709 +Node: Builtin Index586160 +Node: Reserved Word Index593258 +Node: Variable Index595703 +Node: Function Index613116 +Node: Concept Index627111  End Tag Table diff --git a/doc/bash.pdf b/doc/bash.pdf index 1303fb09..eb9f142a 100644 Binary files a/doc/bash.pdf and b/doc/bash.pdf differ diff --git a/doc/bashref.aux b/doc/bashref.aux index fa94a864..5743a0e1 100644 --- a/doc/bashref.aux +++ b/doc/bashref.aux @@ -158,7 +158,7 @@ @xrdef{Modifying Shell Behavior-snt}{Section@tie 4.3} @xrdef{The Set Builtin-title}{The Set Builtin} @xrdef{The Set Builtin-snt}{Section@tie 4.3.1} -@xrdef{Modifying Shell Behavior-pg}{73} +@xrdef{Modifying Shell Behavior-pg}{74} @xrdef{The Set Builtin-pg}{74} @xrdef{The Shopt Builtin-title}{The Shopt Builtin} @xrdef{The Shopt Builtin-snt}{Section@tie 4.3.2} diff --git a/doc/bashref.bt b/doc/bashref.bt index 46dc14dc..054755d9 100644 --- a/doc/bashref.bt +++ b/doc/bashref.bt @@ -35,13 +35,13 @@ \entry{logout}{68}{\code {logout}} \entry{mapfile}{68}{\code {mapfile}} \entry{printf}{68}{\code {printf}} -\entry{read}{69}{\code {read}} +\entry{read}{70}{\code {read}} \entry{readarray}{71}{\code {readarray}} -\entry{source}{71}{\code {source}} -\entry{type}{71}{\code {type}} +\entry{source}{72}{\code {source}} +\entry{type}{72}{\code {type}} \entry{typeset}{72}{\code {typeset}} \entry{ulimit}{72}{\code {ulimit}} -\entry{unalias}{73}{\code {unalias}} +\entry{unalias}{74}{\code {unalias}} \entry{set}{74}{\code {set}} \entry{shopt}{78}{\code {shopt}} \entry{dirs}{112}{\code {dirs}} diff --git a/doc/bashref.bts b/doc/bashref.bts index 40640e63..90717177 100644 --- a/doc/bashref.bts +++ b/doc/bashref.bts @@ -56,7 +56,7 @@ \entry{\code {pushd}}{113} \entry{\code {pwd}}{56} \initial {R} -\entry{\code {read}}{69} +\entry{\code {read}}{70} \entry{\code {readarray}}{71} \entry{\code {readonly}}{56} \entry{\code {return}}{57} @@ -64,19 +64,19 @@ \entry{\code {set}}{74} \entry{\code {shift}}{57} \entry{\code {shopt}}{78} -\entry{\code {source}}{71} +\entry{\code {source}}{72} \entry{\code {suspend}}{128} \initial {T} \entry{\code {test}}{57} \entry{\code {times}}{59} \entry{\code {trap}}{59} \entry{\code {true}}{60} -\entry{\code {type}}{71} +\entry{\code {type}}{72} \entry{\code {typeset}}{72} \initial {U} \entry{\code {ulimit}}{72} \entry{\code {umask}}{60} -\entry{\code {unalias}}{73} +\entry{\code {unalias}}{74} \entry{\code {unset}}{61} \initial {W} \entry{\code {wait}}{128} diff --git a/doc/bashref.cp b/doc/bashref.cp index 22fea2e1..80005c3b 100644 --- a/doc/bashref.cp +++ b/doc/bashref.cp @@ -98,7 +98,7 @@ \entry{prompting}{114}{prompting} \entry{restricted shell}{115}{restricted shell} \entry{POSIX description}{116}{POSIX description} -\entry{POSIX Mode}{116}{POSIX Mode} +\entry{POSIX Mode}{117}{POSIX Mode} \entry{Compatibility Level}{121}{Compatibility Level} \entry{Compatibility Mode}{121}{Compatibility Mode} \entry{job control}{125}{job control} diff --git a/doc/bashref.cps b/doc/bashref.cps index a7b2443d..5be074b0 100644 --- a/doc/bashref.cps +++ b/doc/bashref.cps @@ -105,7 +105,7 @@ \entry{pipeline}{10} \entry{POSIX}{3} \entry{POSIX description}{116} -\entry{POSIX Mode}{116} +\entry{POSIX Mode}{117} \entry{process group}{3} \entry{process group ID}{3} \entry{process substitution}{38} diff --git a/doc/bashref.dvi b/doc/bashref.dvi index 59da4f33..15bd3f7a 100644 Binary files a/doc/bashref.dvi and b/doc/bashref.dvi differ diff --git a/doc/bashref.html b/doc/bashref.html index 01fbff94..bde971b4 100644 --- a/doc/bashref.html +++ b/doc/bashref.html @@ -4,9 +4,9 @@