Чому 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:~$ 

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, якщо на вході менше імен змінних, ніж полів, останній var буде пов'язаний з усіма полями в рядку з роздільниками. Це означає, що $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отримує лише один аргумент. Ваші пробіли з рядка формату надруковані, між ними нічого не заміненого ("як би введено аргумент" null string ").


Я знову протестував 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


Чому така поведінка? це задокументовано?
Шиплу Мокаддім

1
@Shiplu додав абзац про те, де документується поведінка та стандарт, який вимагає поведінки.
PSkocik
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.