читай с историей

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

Как я могу сделать так, чтобы встроенная команда read поддерживала историю, позволяя использовать клавиши вверх/вниз для их перебора?

Я попытался поймать момент нажатия клавиши вверх, однако это, похоже, не работает с read:

read -p '> ' -n 3 foo
echo
echo -n "$foo" | hexdump

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

Чтобы скопировать отличный ответ Майка Стройана из этого старого поста на почтовом списке:

Вы можете использовать “history -r” для чтения файла в историю оболочки
и “history -s” для добавления каждой строки, которую вы прочитали, в историю.
Затем используйте history -w, чтобы сохранить историю обратно в файл. Вот
пример с редактированием в стиле vi.

#!/bin/bash
history -r script_history
set -o vi
CMD=""
while true
do
    echo "Введите что-нибудь"
    read -e CMD
    history -s "$CMD"
    echo "Вы ввели $CMD"
    case "$CMD" in
        stop)
            break
            ;;
        history)
            history
            ;;
    esac
done
history -w script_history
echo остановлено

Вы можете использовать rlwrap для этого, если не против установки программного обеспечения.

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

Вот пример, который может вам подойти:

#!/bin/sh
# Сохраните в rlwrap_example.sh

HISTORY=$HOME/.myscript_history
USERINPUT=$(rlwrap -H $HISTORY sh -c 'read REPLY && echo $REPLY')
echo "Пользователь сказал $USERINPUT"

$ ./rlwrap_example.sh
hello
Пользователь сказал hello

В вышеприведенном скрипте пользователь может использовать все возможности GNU readline, с историей, предоставленной из — и хранящейся в — ~/.myscript_history. Настройте по мере необходимости.

В качестве альтернативы, вы можете использовать read -e в bash, который включает readline для вызовов read, но вы, вероятно, обнаружите, что функциональность его истории слишком ограничена (то есть почти отсутствует).

mrb предлагает сделать это с помощью rlwrap, что действительно очень полезно, но требует немного неаккуратного кода (в общем, в bash некоторые вещи сложны). Согласно мануалу rlwrap, существует более читаемый способ выполнить чтение с историей (по крайней мере с версии 0.43 и выше).
Необходимость вызывать sh -c связана только с тем, что read является встроенной командой. И rlwrap, похоже, ожидает команду, которая существует как файл. Мануал предлагает использовать cat вместо read. Но cat считывает ввод после нажатия клавиши RETURN. Для этого опция -o отправляет EOF после нажатия RETURN. Вот немного измененный пример из мануала:

order=$(rlwrap -o -pYellow -S 'Ваше пицца? ' -H past_orders cat)

В оригинальном примере -o стоит прямо перед cat, и это может вызвать путаницу, как будто cat является его опцией.
В этом примере история сохраняется в текущем каталоге скрипта.

Ответ или решение

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

Проблема с командой read

Команда read в bash по умолчанию не поддерживает навигацию по истории с помощью клавиш стрелок. При нажатии клавиши вверх/вниз будет считан только первый символ кода управления, что делает этот подход неэффективным для работы с историей.

Возможные решения

Вот несколько вариантов, как можно улучшить функциональность read в вашем bash-скрипте.

1. Использование history в сочетании с read

Как предложил Mike Stroyan в своем ответе на рассылке, можно реализовать систему истории, используя команды history и read. Вам понадобятся следующие шаги:

#!/bin/bash
history -r script_history
set -o vi  # или set -o emacs для режима редактирования
CMD=""
while true
do
    echo "Введите команду:"
    read -e CMD
    history -s "$CMD"  # сохраняем команду в историю
    echo "Вы ввели: $CMD"
    case "$CMD" in
        stop)
            break
            ;;
        history)
            history
            ;;
    esac
done
history -w script_history  # сохраняем историю в файл
echo "Завершение работы"

В данном скрипте мы загружаем историю из файла script_history при запуске и сохраняем её обратно перед завершением работы скрипта.

2. Использование rlwrap

Если вас не смущает установка дополнительных утилит, вы можете использовать rlwrap, который оборачивает команды и обеспечивает поддержку истории ввода через интерфейс readline. Ниже приведен пример:

#!/bin/sh
# Сохраните как rlwrap_example.sh

HISTORY=$HOME/.myscript_history
USERINPUT=$(rlwrap -H $HISTORY sh -c 'read REPLY && echo $REPLY')
echo "Пользователь ввел: $USERINPUT"

При запуске этого скрипта у пользователя будет возможность использовать функционал истории благодаря библиотеке GNU readline.

3. Оптимизация использования rlwrap

Согласно документации к rlwrap, его можно упростить для лучшей читаемости. Рассмотрим следующий пример:

order=$(rlwrap -o -p "Желтый" -S 'Ваш заказ? ' -H past_orders cat)

Этот подход делает ваш код более читаемым и устраняет возможные недоразумения с параметрами команд.

Заключение

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

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

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