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