Команда read в zsh вызывает ошибку.

Вопрос или проблема

В 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, можно использовать несколько подходов:

  1. Использование printf и read:
    Это наиболее универсальный метод, позволяющий сохранить работоспособность скрипта как в zsh, так и в других оболочках.

    printf >&2 '%s ' 'erasing all directories (y/n)?'
    read ans
  2. Специфичные для 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
    }
  3. Использование флага -q в zsh:
    zsh поддерживает флаг -q для ответа "да/нет", что позволяет не требовать нажатия клавиши Enter:

    if read -q '?erasing all directories (y/n)? '; then
     rm -rf -- *(D/)
    fi

    Этот метод позволяет выполнить действие сразу после ввода, если пользователь нажал y или Y.

  4. Запуск через 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-стандартов, что повысит их переносимость между различными оболочками.

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

Оцените материал
Добавить комментарий

Капча загружается...