Почему printf выводит больше аргументов, чем ожидалось?

9

Почему этот сценарий оболочки печатает входные данные дважды?

Я ожидал, что скрипт будет игнорировать ввод после 5.

Автор сценария:

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

Вывод:

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:~$ 

И следующий скрипт работает независимо от того, что установлено в $ IFS. Почему?

#! /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"

Вывод:

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:~$ 
mikeserv
источник
остановка printfв любое время с \cэкранированием, связанным со %bспецификатором формата. Как:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv

Ответы:

18

У вас есть три проблемы:

  1. При readусловии, что если во входных данных меньше имен переменных, чем полей, последняя переменная будет привязана ко всем оставшимся полям строки с разделителями. Это означает, что $eпопадает 5 6в ваш первый неожиданный пример.
  2. Поскольку все $a... $eне заключены в кавычки, их значения подвергаются разделению поля . Если $eсодержит " 5 6", то он расширяется до двух аргументов команды.
  3. printfпотребляет все свои аргументы, используя столько же аргументов одновременно, сколько есть %подстановок, многократно. Это похоронено в документации как:

    formatОперанд должен быть повторно так часто , как необходимо для удовлетворения аргументов операндов. Любые дополнительные cили sконверсионные спецификаторы должны оцениваться так, как если бы был задан нулевой строковый аргумент; другие дополнительные спецификации преобразования должны оцениваться так, как если бы был указан нулевой аргумент.

    Другими словами, если есть неиспользуемые аргументы, он начинается заново и обрабатывает их с самого начала, включая всю строку формата. Это полезно, когда вы хотите отформатировать весь массив, скажем:

    printf '%b ' "${array[@]}"

    Ваша printfкоманда получает по одному аргументу от каждого из $a... $d, а затем многие остаются от $e. Когда $eесть " 5 6", printfдва раза, второй просто 6форматируется. Когда это 5 6 7 8 9 10имеет полный спектр замен для второй печати.


Вы можете избежать всего этого, добавив дополнительное фиктивное поле readи заключив в кавычки замену параметров (что всегда является хорошей идеей):

read  a b c d e dummy
printf "> %s %s %s %s %s <" "$a" "$b" "$c" "$d" "$e"

Это даст:

Enter 5 words : 
1 2 3 4 5 6 7 8 9 10
> 1 2 3 4 5 <

dummyполучает все дополнительные поля и printfполучает только пять ожидаемых аргументов.


Ваш второй отредактированный вопрос имеет аналогичный ответ: aполучает значение только тогда, когда IFSнет пробела. Это значит $b... $eрасширяться до нуля, поэтому printfполучает только один аргумент. Ваши пробелы из строки формата печатаются, между ними ничего не подставляется («как если бы был задан аргумент пустой строки»).

Майкл Гомер
источник
Я снова протестировал 2-й скрипт, используя "$ a" ..... "$ e". Второй сценарий снова дает ту же проблему.
3
Цитирование не будет иметь никакого значения для второго сценария. aимеет значение 1 2 3 4 5в виде одной строки и заменяется сразу.
Майкл Гомер
6
 printf "> %s < " 1 2 3

распечатает

 > 1 <> 2 <> 3 <

  printf "> %s %s <" 1 2 3

печать

 > 1 2 <> 3  <

printf съедает все аргументы для удовлетворения его строки формата и затем повторяется, пока все аргументы не будут обработаны.

Второй сценарий работает, потому что только $aназначен, и поэтому команда не переполняется дополнительными итерациями (есть только одна итерация).


Это поведение задокументировано в тексте, поставляемом с help printf:

... Формат используется повторно по мере необходимости, чтобы использовать все аргументы. Если аргументов меньше, чем требует формат, дополнительные спецификации формата ведут себя так, как если бы было предоставлено нулевое значение или пустая строка, в зависимости от ситуации. ...

и поручено http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

PSkocik
источник
Почему это поведение? это задокументировано?
Шиплу Мокаддим
1
@Shiplu добавил параграф о том, где документально оформлено поведение, и о стандарте, который требует такого поведения.
PSkocik