Почему это «в то время как чтение» работает в терминале, но не в сценарии оболочки?

8

Я столкнулся с этой интересной проблемой при заполнении моей панели WM информационным текстом, который применяется путем установки заголовка корневого окна, т.е. xsetroot -name "clever words"

С этой целью печать состояния удачно работает в терминале:

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

Тем не менее, тот же сбой при запуске из сценария оболочки:

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

Производит:

$ sh afilereader
afilereader: 2: читать: arg count

Конечно, это можно исправить, назначив наш результат удачи переменной, затем используя xsetroot с указанной переменной. Но я все еще хотел бы понять, почему это не работает в сценарии.

Я понимаю, что каждая команда по обе стороны конвейера выполняется внутри своей собственной оболочки, но не вижу, как их локализованные переменные могут повлиять на цикл чтения. Или переменные выходят за рамки даже между циклами?

Что мне не хватает?

Обновление:sh я связан с тиром, который находится в процессе сделал POSIX - совместимым. Используя более почтенный bashрешил это.

инвертировать
источник
1
Поведение dash здесь не проявляет никакого отставания от соответствия POSIX. POSIX не требует, чтобы его readможно было вызывать без переменной: pubs.opengroup.org/onlinepubs/9699919799/utilities/read.html
dubiousjim

Ответы:

10

Похоже, вы запускаете первый пример bash, а второй - во всем, на что указывает указатель /bin/sh, - это оболочка POSIX, требующая передачи аргумента, указывающего переменную, в которую вы хотите поместить ввод. Изменение Шебанга #!/bin/bashдолжно исправить это.

Крис Даун
источник
Хорошее наблюдение @Chris! Это работает. Думаю, я немного прочитаю разницу между shи bash.
инвертировать
Действительно ненормальный! Я мог бы вернуться /bin/shк bash, но думаю, что теперь я буду использовать bash напрямую, чтобы избежать двусмысленности. Спасибо :)
инвертировать
Часто интерактивные оболочки отличаются от сценариев оболочки буферизацией их вывода. Чтение из межпроцессных каналов может привести к всевозможным странным временным характеристикам, если команда, генерирующая выходные данные, буферизует свои записи, когда выполняется неинтерактивно.
JesseM
5

В синтаксисе sh вам нужно

IFS= read -r REPLY

Некоторые оболочки, такие как ksh, bash и zsh, позволяют readвызываться без имени переменной, но поведение между ними различается. Смотрите, например, вывод

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

различающиеся по всем bash, zsh, pdksh и ksh93

Стефан Шазелас
источник
Это имеет смысл, почему оболочка, которую я использовал, не знала имя переменной, спасибо @Stephane.
инвертировать