Warum druckt printf mehr Argumente als erwartet?

9

Warum druckt dieses Shell-Skript Eingaben zweimal?

Ich habe erwartet, dass das Skript die Eingaben nach 5 ignoriert.

Skript:

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

Ausgabe:

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

Das folgende Skript funktioniert unabhängig davon, was auf $ IFS eingestellt ist. Warum?

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

Ausgabe:

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
quelle
Stoppen Sie printfjederzeit mit dem \cEscape, das einem %bFormatbezeichner zugeordnet ist. Wie:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
MikeServ

Antworten:

18

Sie haben drei Probleme:

  1. Mit read, wird an alle die übrigen Felder auf der Linie mit Begrenzungszeichen, gebunden, wenn es weniger Variablennamen als Felder in der Eingabe, die letzte var sind. Das heißt, das $ekommt 5 6in Ihr erstes unerwartetes Beispiel.
  2. Da alle $a.. $eunquoted sind, durchlaufen ihre Werte Feldaufspaltung . Wenn $e" 5 6" gilt , wird der Befehl in zwei Argumente erweitert .
  3. printfverbraucht alle seine Argumente und verwendet so viele Argumente gleichzeitig, wie es %Substitutionen gibt, wiederholt. Dies ist in der Dokumentation wie folgt vergraben :

    Der formatOperand wird so oft wie nötig wiederverwendet, um die Argumentoperanden zu erfüllen. Alle zusätzlichen coder sKonvertierungsspezifizierer werden so bewertet, als ob ein Nullzeichenfolgenargument angegeben worden wäre. Andere zusätzliche Konvertierungsspezifikationen sind so zu bewerten, als ob ein Nullargument angegeben worden wäre.

    Mit anderen Worten, wenn nicht verwendete Argumente vorhanden sind, wird von vorne begonnen und auch von Anfang an verarbeitet, einschließlich der gesamten Formatzeichenfolge. Dies ist nützlich, wenn Sie ein gesamtes Array formatieren möchten, z. B.:

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

    Ihr printfBefehl erhält jeweils ein Argument von $a.. $d, und dann bleiben jedoch viele übrig $e. Wenn $e" 5 6" ist, printfhat zwei Runden, die zweite wird gerade 6formatiert. Wenn es ist 5 6 7 8 9 10, hat es die volle Auswahl an Ersetzungen für den zweiten Druck.


Sie können all dies vermeiden, indem Sie ein zusätzliches Dummy-Feld hinzufügen readund Ihre Parametersubstitutionen angeben (was immer eine gute Idee ist):

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

Dies wird geben:

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

dummyRuft alle zusätzlichen Felder ab und printfnur die fünf erwarteten Argumente.


Ihre zweite bearbeitete Frage hat eine ähnliche Antwort: Erhält nur adann einen Wert, wenn IFSkein Leerzeichen vorhanden ist. Das heißt $b.. $eauf nichts erweitern, also printfnur ein einziges Argument bekommen. Ihre Leerzeichen aus der Formatzeichenfolge werden gedruckt, ohne dass dazwischen etwas ersetzt wird ("als ob ein Nullzeichenfolgenargument angegeben wäre").

Michael Homer
quelle
Ich habe das 2. Skript erneut mit "$ a" ..... "$ e" getestet. Das zweite Skript gibt wieder das gleiche Problem.
3
Das Zitieren wird für das zweite Skript keinen Unterschied machen. ahat den Wert 1 2 3 4 5als einzelne Zeichenfolge und wird auf einmal ersetzt.
Michael Homer
6
 printf "> %s < " 1 2 3

wird gedruckt

 > 1 <> 2 <> 3 <

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

druckt

 > 1 2 <> 3  <

printf Frisst alle Argumente auf, um die Formatzeichenfolge zu erfüllen, und wiederholt sie dann, bis alle Argumente verarbeitet sind.

Das zweite Skript funktioniert, weil immer nur $azugewiesen wird und der Befehl daher nicht in zusätzliche Iterationen überläuft (es gibt immer nur eine Iteration).


Dieses Verhalten ist im folgenden Text dokumentiert help printf:

... Das Format wird bei Bedarf wiederverwendet, um alle Argumente zu verwenden. Wenn weniger Argumente vorhanden sind, als das Format erfordert, verhalten sich zusätzliche Formatspezifikationen so, als ob ein Nullwert oder eine Nullzeichenfolge angegeben worden wäre. ...

und wird von http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html beauftragt

PSkocik
quelle
Warum ist dieses Verhalten? ist es dokumentiert?
Shiplu Mokaddim
1
@Shiplu fügte einen Absatz hinzu, in dem beschrieben wird, wo das Verhalten dokumentiert ist und welcher Standard das Verhalten erfordert.
PSkocik