Re: bin/60499: printf(1) doesn't support positional arguments (%n$...)
"Robert Elz via gnats" <[email protected]> Sat, 25 Jul 2026 02:25:01 +0000 (UTC)
| Newsgroups | gmane.os.netbsd.bugs |
|---|---|
| Message-ID | <[email protected]> |
The following reply was made to PR bin/60499; it has been noted by GNATS. From: Robert Elz <[email protected]> To: [email protected] Cc: Subject: Re: bin/60499: printf(1) doesn't support positional arguments (%n$...) Date: Sat, 25 Jul 2026 09:23:12 +0700 Date: Fri, 24 Jul 2026 21:30:01 +0000 (UTC) From: "[email protected] via gnats" <[email protected]> Message-ID: <[email protected]> | printf(1) doesn't seem to support positional parameters "%n$..." | Issue 8 seems to prescribe it: It does. If it didn't, we would most likely have support! The problem is that I simply find the spec in POSIX absurd, so rather that implement that, I implemented (committed really) nothing. Note that the issue doesn't affect any normal use of the things, but would have affected your test case (potentially): | printf '\tR%1$d, G%1$d = R%1$d,\n' $(seq 0 7) That is, when there are more args (8 in that case) than are used by the format string (just 1 here). printf(3) is not affected at all by these issues, it never attempts to repeat the format string, to use up all the args (it has no way to determine how many of them there were) - one potential solution for printf(1) would be to make it the same, so if there are any positional args, the format string would only be used once. The would easily deal with all the issues when the positional args are being used for their original purpose - supporting output of i18n strings, where different cultures output messages with the parameters in different orders, and not necessarily using all of the args (any of them might be omitted). It wouldn't however handle people who have decided that printf(1)'s positional params can be (ab)used to do all kinds of other tricks, like the one you are attempting above, which could just as easily be written for A in $(seq 0 7) do printf '\tR%d, G%d = R%d,\n' "$A" "$A" "$A" done If you do it that way (and something like this is *always* possible in these kinds of cases) there is never any ambiguity about what is intended - there's just a little less opportunity to show off just how clever you are. There are other (not as simple as that one, but still relatively sane) potential solutions, which are not as broken as what POSIX prescribes however -- personally though I kind of like the simple elegance of simply ignoring stray extra args when there are positional params, but none happens to select the final args -- which can easily happen when an internationised printf usage (where the format string comes from a message catalog) and the language in question happens not to need the final arg. printf(3) wouldn't care, printf(1) probably shouldn't either. | $ printf: %1$: invalid directive | R$ | PS: note also, how the format is not checked in advance and the initial | prefix is printed. That is actually a requirement. I agree in most cases a fairly silly one, but in general printf(1) is always required to print everything it has accumulated up to the time an error is detected (including bogus results of conversions in many cases). kre