9

Why is this shell script printing inputs twice?

I expected the script to ignore the inputs after 5.

Script:

#! /bin/bash
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e

Output:

user@linux:~$ pico ifs2.sh
user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 
> 1 2 3 4 5 <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6
> 1 2 3 4 5 <> 6     <user@linux:~$ ./ifs2.sh
Enter 5 words : 
1 2 3 4 5 6 7 8 9 0
> 1 2 3 4 5 <> 6 7 8 9 0 <user@linux:~$ 

And, the following script works no matter what is set to $IFS. Why?

#! /bin/bash    
old="$IFS"
IFS=":"
echo "IFS = $IFS"
echo "Enter 5 words : "
read  a b c d e 
printf "> %s %s %s %s %s <" $a $b $c $d $e    
IFS="$old"

Output:

user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5  
> 1 2 3 4 5      <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1 2 3 4 5
> 1 2 3 4 5     <user@linux:~$ ./ifs2.sh
IFS = :
Enter 5 words : 
1:2:3:4:5
> 1 2 3 4 5 <user@linux:~$ 
  • stop printf at any time with the \c escape associated with a %b format specifier. Like: printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going. – mikeserv Jun 21 '15 at 1:34
18

You have three problems:

  1. 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 gets 5 6 in your first unexpected example.
  2. 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.
  3. 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 extra c or s 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 getting 6 to format. When it's 5 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").

  • I again tested the 2nd script using "$a"....."$e". The second script is giving the same problem again. – user109285 Jun 21 '15 at 0:04
  • 3
    Quoting isn't going to make a difference to the second script. a has the value 1 2 3 4 5 as a single string and it gets substituted all at once. – Michael Homer Jun 21 '15 at 0:13
6
 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

  • Why is this behaviour? is it documented? – Shiplu Mokaddim Nov 18 '15 at 4:51
  • 1
    @Shiplu added a paragraph about where the behavior's documented and the standard that requires the behavior. – PSkocik Nov 18 '15 at 11:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy