выбрать в скрипте

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

В Linux в Bash в скрипте я использую это:

#!/bin/bash

while true; do

    while true; do
        read -r -p 'введите число [4-999]: ' num

        if [[ $num =~ ^([4-9]|[1-9][0-9]{1,2})$ ]]; then
             break
        fi
    done

    while true; do
        result=$(( 5 * num * (1 + (RANDOM%9)) ))
        PS3="Результат $result подходит? "
        select yesno in "да" "изменить введенное число" "сгенерировать новое случайное число" "выйти из этого скрипта" ; do
            case $REPLY in
                1|y) break 3 ;;
                2|n) break 2 ;;
                3|a) break ;;
                4|e) exit ;;

            esac
        done
    done

done

echo ...
echo 'остальная часть скрипта здесь'
echo ...

Есть несколько вопросов о select

я получаю здесь:

1) да                           3) сгенерировать новое случайное число
2) изменить введенное число    4) выйти из этого скрипта

как изменить на

1) да
2) изменить введенное число
3) сгенерировать новое случайное число
4) выйти из этого скрипта

и эта строка корректна?

    select yesno in "да" "изменить введенное число" "сгенерировать новое случайное число" "выйти из этого скрипта" ; do

как использовать клавишу ввода вместо, например, 1?

bash пытается напечатать список в столбиках но использует странный алгоритм.

Похоже, что он пытается напечатать его в столько столбиков, сколько поместится в терминале (узнавая ширину терминала из $COLUMNS или результата ioctl(2, TIOCGWINSZ), если $COLUMNS не установлен¹), за исключением того, что если это приведет к только одной строке, он печатает это в одном столбце вместо этого.

Так что здесь, если вы хотите, чтобы список был в одном столбце независимо от ширины терминала, вы можете установить $COLUMNS в 1 (не 0, для которого bash игнорирует значение и обращается к терминальному устройству) или в очень большое число (COLUMNS=1 или COLUMNS=9999), хотя последнее может быть менее устойчивым к изменениям в будущем.

как использовать клавишу ввода вместо, например, 1?

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

Кстати, в bash для проверки ввода вы не можете использовать диапазоны в выражениях в скобках, таких как ваш [0-9], так как они обычно соответствуют сотням символов, кроме 0123456789. Вам нужно [0123456789] вместо этого. Например, в большинстве локалей ¹²³ не будет отклонен, потому что это символы, которые сортируются между 0 и 9, и вы получите что-то вроде строка 12: ¹²³: ошибка синтаксиса: ожидается операнд (ошибка токен "¹²³") позже, когда это число будет использовано в арифметическом выражении.

Это относительно безвредно здесь, но может стать хуже. Например, в локали th_TH.iso885911 на системе GNU, где (тайский знак 4) находится между 0 и 9, закодирован как один символ и классифицирован как [:alpha:]:

$ LC_ALL=th_TH.iso885911 luit
$ env '๔๔=a[$(reboot)]' bash ./your-script
введите число [4-999]: ๔๔
Система перезагружается...
1) да
2) изменить введенное число
3) сгенерировать новое случайное число
4) выйти из этого скрипта
Результат 0 подходит?

๔๔ не был отклонен, потому что это соответствует [1-9][0-9], и в арифметическом выражении, поскольку именно это требует локаль, считается 2 буквами, был принят как имя переменной, и его значение было развернуто в арифметическом выражении, что вызвало выполнение reboot.

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


¹ при интерактивном запуске bash устанавливает $COLUMNS самостоятельно, а не в скриптах.

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

Вопрос о использовании команды select в Bash

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

Проблема с отображением списка

Вы заметили, что команда select выводит ваш список пунктов в виде нескольких колонок (в вашем случае это: 1) yes, 3) generate a new random number, и так далее). Чтобы изменить это поведение и выводить элементы в одном столбце, вы можете воспользоваться переменной окружения COLUMNS. Установите её значение в 1 перед использованием команды select. Это гарантирует, что пункты будут отображаться в одном столбце.

COLUMNS=1
select yesno in "yes" "change your entered number" "generate a new random number" "exit this script"; do

Замена введения номера на нажатие Enter

К сожалению, команда select не поддерживает использование клавиши Enter вместо ввода числа. Чтобы реализовать подобную логику, вам нужно будет вручную обрабатывать ввод, как вы это делаете при вводе числа. Вот простой пример, как этого можно достичь:

while true; do
    echo "1) yes"
    echo "2) change your entered number"
    echo "3) generate a new random number"
    echo "4) exit this script"
    read -p "Select an option by number (or press Enter for yes): " choice

    if [[ -z "$choice" ]]; then
        choice=1  # Если нажали Enter, ставим по умолчанию "yes"
    fi

    case $choice in
        1) 
            break  # выходит из самого внешнего цикла
            ;;
        2)
            # запросите новое число
            break 2  # выходит из второго цикла
            ;;
        3)
            break  # выходит из текущего цикла для генерации нового числа
            ;;
        4)
            exit  # выхода из скрипта
            ;;
        *)
            echo "Неправильный выбор, попробуйте снова."
            ;;
    esac
done

Вопрос о правильности строки с select

Строка, которую вы предоставили:

select yesno in "yes" "change your entered number" "generate a new random number" "exit this script"; do

в целом корректна, если она дополнена правильной обработкой выбора, как описано выше.

Ввод данных и проверка чисел для безопасности

Как упоминалось в описании, стоит обратить внимание на то, как вы выполняете валидацию чисел в вашем скрипте. Например, использование:

if [[ $num =~ ^([4-9]|[1-9][0-9]{1,2})$ ]]; then

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

Для получения чисел между 4 и 999 в безопасной форме вы можете использовать:

if [[ $num =~ ^[4-9]$|^[1-9][0-9]{1,2}$|^1000$ ]]; then

Заключение

Ответы на ваши вопросы касательно использования команды select в Bash обеспечивают более глубокое понимание её работы. Настройка режима отображения, использование клавиши Enter и дугой аспект безопасности ввода в ваших скриптах позволит вам создать более надежное и юзер-френдли приложение на Bash. Убедитесь, что ваши сценарии тестируются на разных локалях, чтобы избежать неожиданных результатов.

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

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