Pourquoi printf imprime-t-il plus d'arguments que prévu?

9

Pourquoi ce script shell imprime-t-il deux fois les entrées?

Je m'attendais à ce que le script ignore les entrées après 5.

Scénario:

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

Production:

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

Et, le script suivant fonctionne quel que soit le paramètre défini sur $ IFS. Pourquoi?

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

Production:

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
la source
arrêter printfà tout moment avec l' \céchappement associé à un %bspécificateur de format. Comme:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv

Réponses:

18

Vous avez trois problèmes:

  1. Avec read, s'il y a moins de noms de variables que de champs dans l'entrée, la dernière var sera liée à tous les champs restants de la ligne, avec des délimiteurs. Cela signifie que cela $eentre 5 6dans votre premier exemple inattendu.
  2. Parce que tous $a.. ne $esont pas cités, leurs valeurs subissent une division de champ . Si $econtient " 5 6", il se développe en deux arguments à la commande.
  3. printfconsomme tous ses arguments, en utilisant autant d'arguments à la fois qu'il y a de %substitutions, à plusieurs reprises. Ceci est enterré dans la documentation comme:

    L' formatopérande doit être réutilisé aussi souvent que nécessaire pour satisfaire les opérandes d'argument. Tout spécificateur supplémentaire cou de sconversion doit être évalué comme si un argument de chaîne nulle était fourni; les autres spécifications de conversion supplémentaires doivent être évaluées comme si un argument zéro était fourni.

    En d'autres termes, s'il y a des arguments inutilisés, il recommence et les traite aussi depuis le début, y compris la chaîne de formatage entière. Ceci est utile lorsque vous souhaitez formater un tableau entier, par exemple:

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

    Votre printfcommande obtient un argument de chacun de $a.. $d, et ensuite, il en reste beaucoup $e. Quand $eest " 5 6", il y en printfa deux, le second se met simplement 6au format. Quand c'est, 5 6 7 8 9 10il a la gamme complète de substitutions pour la deuxième impression.


Vous pouvez éviter tout cela en ajoutant un champ factice supplémentaire à read, et en citant vos substitutions de paramètres (ce qui est toujours une bonne idée):

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

Cela donnera:

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

dummyobtient tous les champs supplémentaires et printfn'obtient que les cinq arguments que vous attendiez.


Votre deuxième question modifiée a une réponse similaire: an'obtient une valeur que si elle IFSn'a pas d'espace. Cela signifie que $b$ene rien développer, donc printfn'obtient qu'un seul argument. Vos espaces de la chaîne de format sont imprimés, sans rien remplacer entre eux ("comme si un argument de chaîne nulle était fourni").

Michael Homer
la source
J'ai de nouveau testé le 2ème script en utilisant "$ a" ..... "$ e". Le deuxième script donne à nouveau le même problème.
3
Citer ne changera rien au second script. aa la valeur 1 2 3 4 5comme une chaîne unique et elle est remplacée d'un seul coup.
Michael Homer
6
 printf "> %s < " 1 2 3

imprimera

 > 1 <> 2 <> 3 <

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

impressions

 > 1 2 <> 3  <

printf mange tous les arguments pour satisfaire sa chaîne de format, puis se répète jusqu'à ce que tous les arguments soient traités.

Le deuxième script fonctionne car seul $aest jamais affecté à et donc la commande ne déborde pas dans des itérations supplémentaires (il n'y a qu'une seule itération).


Ce comportement est documenté dans le texte fourni avec help printf:

... Le format est réutilisé si nécessaire pour consommer tous les arguments. S'il y a moins d'arguments que le format ne l'exige, les spécifications de format supplémentaires se comportent comme si une valeur nulle ou une chaîne nulle, selon le cas, avait été fournie. ...

et est mandaté par http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

PSkocik
la source
Pourquoi ce comportement? est-il documenté?
Shiplu Mokaddim
1
@Shiplu a ajouté un paragraphe sur l'endroit où le comportement est documenté et la norme qui requiert le comportement.
PSkocik