Pourquoi ce «pendant la lecture» fonctionne-t-il dans un terminal, mais pas dans un script shell?

8

Je suis tombé sur ce problème intéressant en remplissant ma barre WM avec du texte d'informations, qui est appliqué en définissant le titre de la fenêtre racine, c'est-à-dire xsetroot -name "clever words"

À cette fin, l'impression d'une fortune fonctionne très bien dans un terminal:

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

Pourtant, ce même échoue lorsqu'il est exécuté à partir d'un script shell:

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

Produit:

$ sh afilereader
afilereader: 2: read: arg count

Bien sûr, cela est résolu en affectant notre résultat de fortune à une variable, puis en utilisant xsetroot avec ladite variable. Mais j'aimerais quand même comprendre pourquoi cela ne fonctionne pas dans un script.

Je me rends compte que chaque commande de chaque côté du pipeline est exécutée dans son propre sous-shell, mais je ne vois pas comment leurs variables localisées pourraient affecter la boucle de lecture while. Ou les variables sont-elles hors de portée, même entre les itérations de boucle?

Qu'est-ce que je rate?

Mise à jour: le que shj'ai utilisé est lié au tiret, qui est en cours de mise en conformité POSIX. L'utilisation du plus vénérable a bashrésolu ce problème.

inverser
la source
1
Le comportement de dash ici ne manifeste aucun défaut de conformité POSIX. POSIX ne nécessite pas d' readêtre invocable sans variable: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html
dubiousjim

Réponses:

10

Vous semblez exécuter le premier exemple dans bash, et le second dans tout ce qui est indiqué par /bin/sh, qui est un shell POSIX nécessitant un argument à passer spécifiant la variable dans laquelle vous souhaitez mettre l'entrée. Changer le shebang en #!/bin/bashdevrait résoudre ce problème.

Chris Down
la source
Bonne observation @Chris! Ça marche. Je pense que je vais lire un peu sur la différence entre shet bash.
inverser
Anormal en effet! Je pourrais relier /bin/shà bash, mais je pense que j'utiliserais bash directement à partir de maintenant, pour éviter toute ambiguïté. Merci :)
inverser
Les shells souvent interactifs diffèrent des scripts shell par la mise en mémoire tampon de leur sortie. La lecture à partir de canaux interprocessus peut entraîner toutes sortes de comportements de synchronisation étranges si la commande qui génère la sortie met en tampon ses écritures lorsqu'elle est exécutée de manière non interactive.
JesseM
5

Dans la syntaxe sh, vous avez besoin

IFS= read -r REPLY

Certains shells comme ksh, bash et zsh permettent readd'être appelés sans nom de variable mais le comportement diffère entre eux. Voir par exemple la sortie de

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

différent sur tous les bash, zsh, pdksh et ksh93

Stéphane Chazelas
la source
Cela explique pourquoi le shell que j'utilisais ne connaissait pas le nom de la variable, merci @Stephane.
inverser