¿Por qué printf imprime más argumentos de lo esperado?

9

¿Por qué este script de shell imprime entradas dos veces?

Esperaba que el script ignorara las entradas después de 5.

Guión:

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

Salida:

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

Y, el siguiente script funciona sin importar lo establecido en $ IFS. ¿Por qué?

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

Salida:

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
fuente
detenerse printfen cualquier momento con el \cescape asociado con un %bespecificador de formato. Así como:printf %s%\ d%b thing 3 "${var+\cquit printing if set}\nelse do a newline" and 0 keep\ going.
mikeserv

Respuestas:

18

Tienes tres problemas:

  1. Con read, si hay menos nombres de variables que campos en la entrada, la última var estará vinculada a todos los campos restantes en la línea, con delimitadores. Eso significa que $eentra 5 6en su primer ejemplo inesperado.
  2. Debido a que todos $a... $eno están entre comillas, sus valores sufren división de campo . Si $econtiene " 5 6", se expande en dos argumentos para el comando.
  3. printfconsume todos sus argumentos, usando tantos argumentos a la vez como %sustituciones, repetidamente. Esto está enterrado en la documentación como:

    El formatoperando se reutilizará tantas veces como sea necesario para satisfacer los operandos del argumento. Cualquier especificador adicional co de sconversión se evaluará como si se suministrara un argumento de cadena nulo; otras especificaciones de conversión adicionales se evaluarán como si se proporcionara un argumento cero.

    En otras palabras, si hay argumentos no utilizados, comienza de nuevo y los procesa desde el principio, incluida toda la cadena de formato. Esto es útil cuando desea formatear una matriz completa, por ejemplo:

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

    Su printfcomando obtiene un argumento de cada uno de $a... $dy luego, sin embargo, quedan muchos $e. Cuando $ees " 5 6", printftiene dos vueltas, la segunda solo 6formateando. Cuando es 5 6 7 8 9 10tiene la gama completa de sustituciones para la segunda impresión.


Puede evitar todo esto agregando un campo ficticio adicional ready citando las sustituciones de sus parámetros (que siempre es una buena idea):

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

Esto le dará:

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

dummyobtiene todos los campos adicionales y printfsolo obtiene los cinco argumentos que esperaba.


Su segunda pregunta editada tiene una respuesta similar: solo aobtiene un valor cuando IFSno tiene un espacio. Eso significa $b... $eexpandirse a nada, por lo que printfsolo obtiene un argumento único. Sus espacios de la cadena de formato se imprimen, sin nada sustituido entre ellos ("como si se suministrara un argumento de cadena nula").

Michael Homer
fuente
Nuevamente probé el segundo script usando "$ a" ..... "$ e". El segundo script está dando el mismo problema nuevamente.
3
Citar no va a hacer una diferencia en el segundo guión. atiene el valor 1 2 3 4 5como una sola cadena y se sustituye de una vez.
Michael Homer
6
 printf "> %s < " 1 2 3

imprimirá

 > 1 <> 2 <> 3 <

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

huellas dactilares

 > 1 2 <> 3  <

printf se come todos los argumentos para satisfacer su cadena de formato y luego se repite hasta que se procesen todos los argumentos.

El segundo script funciona porque solo $ase asigna y, por lo tanto, el comando no se desborda en iteraciones adicionales (solo hay una iteración).


Este comportamiento está documentado en el texto provisto con help printf:

... El formato se reutiliza según sea necesario para consumir todos los argumentos. Si hay menos argumentos de los que requiere el formato, las especificaciones de formato adicionales se comportan como si se hubiera proporcionado un valor cero o una cadena nula, según corresponda. ...

y tiene el mandato de http://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html

PSkocik
fuente
¿Por qué es este comportamiento? ¿está documentado?
Shiplu Mokaddim
1
@Shiplu agregó un párrafo sobre dónde se documenta el comportamiento y el estándar que requiere el comportamiento.
PSkocik