Вопрос или проблема
В zsh выполнение команды read -p 'erasing all directories (y/n) ?' ans
вызывает ошибку,
read: -p: no coprocess
Но в bash выводится запрос. Как это сделать в zsh?
Вы можете по-прежнему использовать read
, просто вам нужно сначала вывести запрос. В zsh
, -p
указывает, что ввод должен считываться из сопроцесса, вместо указания запроса.
Вы можете сделать следующее, что является совместимым с POSIX:
printf >&2 '%s ' 'erase all directories? (y/n)'
read ans
Как и для read 'var?prompt'
в ksh/zsh или read -p prompt var
в bash, запрос выводится в stderr, чтобы не загрязнять нормальный вывод вашего скрипта.
или более zsh-способ
() {
local compcontext="yn:yes or no:(y n)"
vared -cp 'erasing all directories (y/n) ? ' ans
}
Который позволяет завершать ответ.
То же самое, что и в ksh
:
IFS= read -r 'ans?erasing all directories (y/n)? '
Также обратите внимание, что read
в zsh
имеет -q
для ответов “да/нет”:
if read -q '?erasing all directories (y/n)? '; then
rm -rf -- *(D/)
fi
Он возвращает true, если вы вводите “да” и не требует нажатия Enter.
Я только что столкнулся с этой же ошибкой.
Интерпретатор на моем Mac изменился С bash
НА zsh
, тем самым сломав мою команду, которую я периодически использую для очистки резервных копий в моей рабочей среде:
Сломано:
read -s -p PASSWORD;echo ${REPLY} | ssh [email protected] "sudo -S rm /mnt/backups/config_*.tgz"
Исправлено:
Просто добавьте перед командой bash
😉 :
bash read -s -p PASSWORD;echo ${REPLY} | ssh [email protected] "sudo -S rm /mnt/backups/config_*.tgz"
Должно сработать как исправление для любых других сбоев в zsh-командах, написанных для bash
HTH-
Ответ или решение
Проблема, с которой вы столкнулись при использовании команды read
в zsh
, связана с различиями в реализации read
между оболочками bash
и zsh
. В bash
флаг -p
используется для вывода подсказки перед чтением ввода пользователя. Однако в zsh
флаг -p
связан с использованием канала взаимодействия с сопрограммами и поэтому вызывает ошибку "read: -p: no coprocess", если применяется вне контекста сопрограмм.
Теория
Разница между поведением команд read
в bash
и zsh
обусловлена тем, как каждая оболочка трактует синтаксис и опции. bash
является более распространенной оболочкой и многие скрипты написаны именно под нее. Это может создать трудности при переходе на zsh
, которая является более современной оболочкой с дополнительными возможностями, но отличается нюансами в некоторых командах.
Пример
Как видно из описания проблемы, в bash
команда read -p 'erasing all directories (y/n) ?' ans
работает корректно и выводит сообщение перед получением ввода. Однако в zsh
возникает ошибка. Чтобы решить эту проблему, достаточно изменить подход к выводу подсказки. Вместо использования опции -p
, можно сначала вывести сообщение, используя printf
, а затем вызвать команду read
, чтобы получить ввод пользователя.
Для примера, можно использовать следующий код:
printf >&2 '%s ' 'erase all directories (y/n)?'
read ans
Здесь используется printf
для вывода сообщения в стандартный поток ошибок (stderr), чтобы не загрязнять стандартный вывод (stdout), что соответствует POSIX-стандартам.
Применение
Чтобы адаптировать ваш скрипт, написанный под bash
, к работе в zsh
, можно использовать несколько подходов:
-
Использование
printf
иread
:
Это наиболее универсальный метод, позволяющий сохранить работоспособность скрипта как вzsh
, так и в других оболочках.printf >&2 '%s ' 'erasing all directories (y/n)?' read ans
-
Специфичные для
zsh
функции:
zsh
предлагает расширенные возможности для работы с вводом. Например:IFS= read -r 'ans?erasing all directories (y/n)? '
Или еще один способ с использованием
vared
, который позволяет использовать автодополнение:() { local compcontext="yn:yes or no:(y n)" vared -cp 'erasing all directories (y/n) ? ' ans }
-
Использование флага
-q
вzsh
:
zsh
поддерживает флаг-q
для ответа "да/нет", что позволяет не требовать нажатия клавиши Enter:if read -q '?erasing all directories (y/n)? '; then rm -rf -- *(D/) fi
Этот метод позволяет выполнить действие сразу после ввода, если пользователь нажал
y
илиY
. -
Запуск через
bash
:
Если у вас есть много скриптов, написанных именно дляbash
, и вы хотите сохранить прежнее их поведение без значительных изменений, можно запускать отдельные команды или скрипты черезbash
.bash -c 'read -p "PASSWORD"; echo ${REPLY} | ssh <email> "sudo -S rm /mnt/backups/config_*.tgz"'
Заключение
Когда происходит миграция с bash
на zsh
, довольно часто можно столкнуться с несовместимостями в скриптах. Чтобы избежать подобных проблем в будущем, рекомендуется:
- Изучить особенности
zsh
: Оболочкаzsh
предлагает множество интересных возможностей и улучшений функциональности, что может быть полезно в написании более сложных и эффективных скриптов. - Соблюдать POSIX-совместимость: По возможности, пишите скрипты с учетом POSIX-стандартов, что повысит их переносимость между различными оболочками.
В долгосрочной перспективе, грамотная адаптация и понимание особенностей каждого инструмента помогут избежать ошибок и повысить устойчивость программного обеспечения к изменениям окружающей среды.