3

I am currently looking for an alternative to the following code that works a little less 'wonky'.

printf "Please enter the ticket number:\t"; read vTICKET; vTICKET=$(printf %04d "$vTICKET"); printf "$vTICKET\n";

If I input 072 as the input, this is what I see

Please enter the ticket number: 072
0058

I am wondering if there is another way I can be a little more forgiving on the input or with the read command? printf seemed like the cool way to add leading zeroes without actually testing string length.

Jeff Schaller
  • 66,199
  • 35
  • 114
  • 250
Michael
  • 33
  • 4

2 Answers2

5

The leading zeros on the input value are causing the shell to interpret it as an octal number.

You can force decimal conversion using 10# e.g.

$ printf "Please enter the ticket number:\t"; read vTICKET; vTICKET=$(printf %04d "$((10#$vTICKET))" ); printf "$vTICKET\n";
Please enter the ticket number: 072
0072

Note that in bash, you can assign the results of a printf to a variable directly using -v e.g. printf -v vTICKET %04d "$((10#$vTICKET))"

See also How do I stop Bash from interpreting octal code instead of integer?

steeldriver
  • 78,509
  • 12
  • 109
  • 152
  • Do we even need `%04d` and the accompanying interpretation? Wouldn't `%04s` be enough? – muru Jun 12 '19 at 10:03
  • @muru hmm - is zero-padding supported for non-numeric conversion specifiers? I couldn't get that to work for me (bash 4.4.19) – steeldriver Jun 12 '19 at 23:18
  • Ah, it's not portable. On macOS (presumably other BSDs), it works. Seems to based on what `printf(3)` does. Compiling a C file with `printf("%04s", "072");`, I get a warning from `clang` (nothing from GCC 9): ``flag '0' results in undefined behavior with 's' conversion specifier [-Wformat]``, but it prints `0072` all the same. – muru Jun 13 '19 at 01:48
  • @muru interesting... with both `clang 6.0.0-1ubuntu2` and `gcc 7.4.0-1ubuntu1~18.04`, C printf appears to ignore the `0` and pad to the specified width with spaces – steeldriver Jun 13 '19 at 01:59
2

In Bash, you can get rid of any prefixing zeros. After setting on extglob first:

$ shopt -s extglob

You can use *(0) after ## in parameter expansion:

$ read -r TICKET; printf "%.4d\n" "${TICKET##*(0)}"
000000000000000000000000000000000000072
0072

Once finished, you can go back to normal with extglob:

$ shopt -u extglob

Now *(0) undergoes the basic pathname expansion again:

$ read -r TICKET; printf "%.4d\n" "${TICKET##*(0)}"
asdfasdf(0)72
0072
$ read -r TICKET; printf "%.4d\n" "${TICKET##*(0)}"
asdfasdf072
bash: printf: asdfasdf072: invalid number
0000