¿Por qué funciona esto "mientras se lee" en un terminal, pero no en un script de shell?

8

Me encontré con este interesante problema mientras llenaba mi barra WM con texto de información, que se aplica configurando el título de la ventana raíz, es decir xsetroot -name "clever words"

Para este fin, imprimir una fortuna funciona bien en una terminal:

fortune -s | while read -r; do xsetroot -name "$REPLY"; done

Sin embargo, eso mismo falla cuando se ejecuta desde un script de shell:

#!/bin/sh
cat /tmp/afile | while read; do echo "$REPLY"; done

Produce:

$ sh afilereader
afilereader: 2: read: arg count

Por supuesto, esto se soluciona asignando nuestro resultado de fortuna a una variable, luego usando xsetroot con dicha variable. Pero todavía me gustaría entender por qué esto no funciona en un script.

Me doy cuenta de que cada comando a cada lado de la tubería se ejecuta dentro de su propio subshell, pero no veo cómo sus variables localizadas podrían afectar el ciclo while read. ¿O las variables están fuera del alcance incluso entre las iteraciones del bucle?

¿Qué me estoy perdiendo?

Actualización: Lo shque utilicé está vinculado al guión, que está en proceso de cumplir con POSIX. Usando el más venerable bashresuelto esto.

invertir
fuente
1
El comportamiento de dash aquí no manifiesta ningún incumplimiento del POSIX. POSIX no requiere que readsea ​​invocable sin una variable: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html
dubiousjim

Respuestas:

10

Parece que está ejecutando el primer ejemplo bash, y el segundo en lo que sea que señale /bin/sh, que es un shell POSIX que requiere que se pase un argumento que especifique la variable en la que desea ingresar la entrada. Cambiar el shebang a #!/bin/bashdebería solucionar esto.

Chris Down
fuente
Buena observación @Chris! Eso funciona. Creo que leeré un poco sobre la diferencia entre shy bash.
invertir
Anormal de hecho! Podría volver /bin/sha vincular a bash, pero creo que solo usaría bash directamente de ahora en adelante, para evitar la ambigüedad. Gracias :)
invierta el
Con frecuencia, los shells interactivos difieren de los scripts de shell en el almacenamiento de su salida. La lectura de tuberías entre procesos puede dar lugar a todo tipo de comportamientos de temporización extraños si el comando que genera la salida amortigua sus escrituras cuando se ejecuta de forma no interactiva.
JesseM
5

En la sintaxis sh, necesitas

IFS= read -r REPLY

Algunos shells como ksh, bash y zsh permiten readser llamados sin un nombre de variable, pero el comportamiento difiere entre ellos. Ver por ejemplo la salida de

printf 'te\ st\\\na ' | "$shell" -c 'read; printf "%s\n" "<$REPLY>"'

que difieren en todos los bash, zsh, pdksh y ksh93

Stéphane Chazelas
fuente
Eso tiene sentido por qué el shell que estaba usando no conocía el nombre de la variable, gracias @Stephane.
invertir el