Why does printf print more arguments than expected?
You have three problems:
- With
read
, if there are fewer variable names than fields in the input, the last var will be bound to all the remaining fields on the line, with delimiters. That means that$e
gets5 6
in your first unexpected example. - Because all of
$a
..$e
are unquoted, their values undergo field splitting. If$e
holds "5 6
" then it expands into two arguments to the command. printf
consumes all its arguments, using as many arguments at once as there are%
substitutions, repeatedly. This is buried in the documentation as:The
format
operand shall be reused as often as necessary to satisfy the argument operands. Any extrac
ors
conversion specifiers shall be evaluated as if a null string argument were supplied; other extra conversion specifications shall be evaluated as if a zero argument were supplied.In other words, if there are unused arguments it starts over again and processes them from the beginning too, including the whole format string. This is useful when you want to format an entire array, say:
printf '%b ' "${array[@]}"
Your
printf
command gets one argument from each of$a
..$d
, and then however many are left over from$e
. When$e
is "5 6
",printf
has two goes around, the second just getting6
to format. When it's5 6 7 8 9 10
it has the full range of substitutions for the second printing.
You can avoid all of these by adding an extra dummy field to read
, and quoting your parameter substitutions (which is always a good idea):
read a b c d e dummy
printf "> %s %s %s %s %s <" "$a" "$b" "$c" "$d" "$e"
This will give:
Enter 5 words :
1 2 3 4 5 6 7 8 9 10
> 1 2 3 4 5 <
dummy
gets all the extra fields, and printf
only gets the five arguments you expected.
Your second edited-in question has a similar answer: only a
gets a value when IFS
doesn't have a space. That means $b
..$e
expand to nothing, so printf
only gets a single argument. Your spaces from the format string are printed, with nothing substituted in between them ("as if a null string argument were supplied").
printf "> %s < " 1 2 3
will print
> 1 <> 2 <> 3 <
printf "> %s %s <" 1 2 3
prints
> 1 2 <> 3 <
printf
eats up all the arguments to satisfy its format string and then repeats until all arguments are processed.
The second scripts works because only $a
is ever assigned to and therefore the command doesn't overflow into additional iterations (there's only ever one iteration).
This behavior is documented in the text provided with help printf
:
... The format is re-used as necessary to consume all of the arguments. If there are fewer arguments than the format requires, extra format specifications behave as if a zero value or null string, as appropriate, had been supplied. ...
and is mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html