Dlaczego printf wypisuje więcej argumentów niż oczekiwano?

9

Dlaczego dane wejściowe tego skryptu powłoki są drukowane dwukrotnie?

Spodziewałem się, że skrypt zignoruje dane wejściowe po 5.

Scenariusz:

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

Wynik:

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

I następujący skrypt działa bez względu na ustawienie $ IFS. Dlaczego?

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

Wynik:

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
źródło
zatrzymaj się printfw dowolnym momencie dzięki \cwyjściu związanemu ze %bspecyfikatorem formatu. Jak:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv

Odpowiedzi:

18

Masz trzy problemy:

  1. Za pomocą read, jeśli na wejściu jest mniej nazw zmiennych niż pól, ostatni zmienny zostanie powiązany ze wszystkimi pozostałymi polami w linii, z ogranicznikami. Oznacza to, że $edostaniesz 5 6się do pierwszego nieoczekiwanego przykładu.
  2. Ponieważ wszystkie $a... $enie są cytowane, ich wartości podlegają podziałowi pól . Jeśli $ezawiera „ 5 6”, to rozwija się do dwóch argumentów polecenia.
  3. printfzużywa wszystkie argumenty, używając tyle argumentów na raz, ile jest %podstawień, wielokrotnie. Jest to zakopane w dokumentacji jako:

    formatOperand musi być ponownie tak często, jak to konieczne do zaspokojenia argumentów argumentów. Wszelkie dodatkowe club sspecyfikatory konwersji należy oceniać tak, jakby podano argument o wartości zerowej; inne dodatkowe specyfikacje konwersji ocenia się tak, jakby podano argument zerowy.

    Innymi słowy, jeśli są nieużywane argumenty, zaczyna się od nowa i przetwarza je od początku, łącznie z ciągiem całego formatu. Jest to przydatne, gdy chcesz sformatować całą tablicę, powiedz:

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

    Twoje printfpolecenie pobiera jeden argument z każdego z $a... $d, a następnie wiele z nich zostało $e. Kiedy $ejest „ 5 6”, printfma dwa dookoła, drugi właśnie się 6formatuje. Kiedy 5 6 7 8 9 10ma pełny zakres zamienników drugiego wydruku.


Możesz tego wszystkiego uniknąć, dodając dodatkowe pole readzastępcze i podając swoje podstawienia parametrów (co zawsze jest dobrym pomysłem):

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

To da:

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

dummypobiera wszystkie dodatkowe pola i printfotrzymuje tylko pięć oczekiwanych argumentów.


Drugie edytowane pytanie ma podobną odpowiedź: aotrzymuje wartość tylko wtedy, gdy IFSnie ma spacji. Oznacza to $b... $erozwinąć do zera, więc printfdostaje tylko jeden argument. Spacje z ciągu formatu są drukowane, a między nimi nic nie jest podstawiane („jak gdyby podano argument ciąg pusty”).

Michael Homer
źródło
Ponownie przetestowałem drugi skrypt za pomocą „$ a” ..... „$ e”. Drugi skrypt ponownie daje ten sam problem.
3
Cytowanie nie zrobi różnicy w drugim skrypcie. ama wartość 1 2 3 4 5jako pojedynczy ciąg i zostaje zastąpiony jednocześnie.
Michael Homer
6
 printf "> %s < " 1 2 3

wydrukuje

 > 1 <> 2 <> 3 <

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

odciski

 > 1 2 <> 3  <

printf zjada wszystkie argumenty, aby spełnić ciąg formatu, a następnie powtarza się, aż wszystkie argumenty zostaną przetworzone.

Drugi skrypt działa, ponieważ tylko $ado niego przypisano i dlatego polecenie nie przepełnia się dodatkowymi iteracjami (istnieje tylko jedna iteracja).


To zachowanie jest udokumentowane w tekście dostarczonym z help printf:

... Format jest ponownie używany w razie potrzeby do wykorzystania wszystkich argumentów. Jeśli argumentów jest mniej niż wymaga format, dodatkowe specyfikacje formatu zachowują się tak, jakby podano wartość zerową lub łańcuch zerowy, odpowiednio. ...

i jest upoważniony przez http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

PSkocik
źródło
Dlaczego to zachowanie? czy jest to udokumentowane?
Shiplu Mokaddim,
1
@Shiplu dodał akapit o tym, gdzie zachowanie jest udokumentowane i standard, który wymaga zachowania.
PSkocik,