Date:        Sat, 8 Feb 2025 13:21:34 +1000
    From:        Martin D Kealey <mar...@kurahaupo.gen.nz>
    Message-ID:  
<can_u6mwpq7r7jy+ocubhdwo7xfkrc-z7xfhz0t7-6m9aq_a...@mail.gmail.com>


  | While POSIX doesn't directly specify what happens when converting
  | an empty-string to a number,

It also doesn't say what should be done when converting %%% into a number
either, but I doubt you're seriously going to claim that there should be
no warning from
        printf %d\\n '%%%'
(quotes not really required, never mind).

What you didn't quote was from the specification of what is
required (when an arg is present):

        The argument operands shall be treated as strings if the
        corresponding conversion specifier is b, c, or s, and shall
        be evaluated as if by the strtod( ) function if the corresponding
        conversion specifier is a, A, e, E, f, F, g, or G.

%d is none of the above, so:

        Otherwise, they shall be evaluated as unsuffixed C integer
        constants, as described by the ISO C standard, with the following
        extensions:

The extensions are not relevant here, and '' is not a C integer constant.
Hence, it is not valid.

  | it does seem very strange indeed to treat "missing" more harshly
  | than "empty".

Not at all.   Missing just means there is no more data.  An invalid value
means the code screwed up somehow, and provided bad data.

  | which is then followed by a table of examples that does *not* include the
  | empty string.

Along with large numbers of other invalid strings of varying kinds
(like say "2+2") which also aren't included.   

  | and if the correspondence with strtol() is to be taken at face value,
  | this would likewise imply that empty string is a valid representation for
  | zero, since strtol() reports that it has converted the entire string in
  | that case.

Nonsense.   If one does:

        errno = 0;
        num = strtol(str, &ep, 10 /* or any other valid base, incl 0 */);

then strtol has failed if any of the following are true:

        errno != 0              /* typically ERANGE from overflow */
        ep == str               /* no digits were converted, cannot be a C 
const */
        *ep != '\0'             /* the whole string was not converted */

which of those an application might treat as errors are up to it (the final
one often isn't, as some applications allow a suffix (like K M ...) or for
there to be some other unrelated data following the number which is parsed
in some other way.

For printf(1) all three of them generate warnings.   So would the case
(which strtol() allows) where there are leading spaces before the value,
that's not a C integer constant either.   Bash doesn't get that one correct
either, and that should also be fixed.   NetBSD's printf says:

        printf: Arg 1: '  1' numeric value required

to stderr (still prints 1 as the result of the %d conversion).

Give up, you're wasting your time arguing this.

  | But we can write «printf 'foo=%d\n' "$foo"» where foo is the result of a
  | previous string manipulation that could yield an empty (not unset) result.

If you want it to be printed as an integer, then it needs to be an
integer, and any string manipulation which produces something else is
broken.   Note that as all args are strings, you could print it using %s
if you wanted, then you'd get no warning (you wouldn't get '0' either, but
as you seem to believe that nothing is equivalent to 0, that shouldn't
bother you).

You could use what you're doing now (though I don't understand how
that works, clearly yet another bash extension I have ignored), that
is (apparently) ${x##*(0)} but write it as
        $(( ${x##*(0)} -0 ))
If the var expansion produces a number, subtract 0 from it, which changes
nothing, if it produces '' then we have $((-0)) which is just 0.

There are a gazillion different ways of getting rid of the octal
conversion "problem", one I prefer in cases where I know the value
is 2 digits, always, but might be 0n for 0<=n<=9, is  $(( 1$x - 100 ))

  | Failing all that, if you're dead-set on having this, please (a) send the
  | warning to BASH_XTRACEFD, so that it can be suppressed without redirecting
  | stderr;

printf is a separate utility (even if it is built into bash), it knows
nothing of BASH_XTRACEFD (consider what would happen if someone added a
"disable printf" to your script, and ran /usr/bin/printf (using whatever
path locates the external command) - that one only has stdin stdout and
stderr available to it (usually).   Further posix requires these warnings
to be sent to stderr.

kre

ps: I fixed NetBSD's printf to do this correctly a while ago.   That has
produced exactly 0 complaints about scripts now failing which used
to work.    Or should I instead say: that has produced exactly  complaints
about ... ?

Reply via email to