Я хочу приостановить ввод в сценарии оболочки и предложить пользователю выбор.
Стандартный вопрос Yes, No или Cancel.
Как мне сделать это в обычном приглашении bash?
Ответ 1
Самым простым и широко доступным методом получения пользовательского ввода в интерпретаторе shell является команда read. Лучший способ проиллюстрировать ее использование — это простая демонстрация:
while true; do
read -p "Хотите ли вы установить эту программу?" yn
case $yn in
[Yy]* ) make install; break;;
[Nn]* ) exit;;
* ) echo "Пожалуйста, ответьте да или нет.";;
esac
done
Другой метод — это команда select в Bash. Вот тот же пример с использованием команды select:
echo " Хотите ли вы установить эту программу?"
select yn in "Да" "Нет"; do
case $yn in
Yes ) make install; break;;
No ) exit;;
esac
done
При использовании select вам не нужно корректировать вводимые данные — она отображает доступные варианты, а вы вводите число, соответствующее вашему выбору. Она также автоматически зацикливается, поэтому нет необходимости в цикле while true для повторных попыток в случае ввода неверных данных.
Кроме того, есть способ сделать язык запроса независимым. Адаптация первого примера для лучшего обслуживания нескольких языков может выглядеть следующим образом:
set -- $(locale LC_MESSAGES)
yesptrn="$1"; noptrn="$2"; yesword="$3"; noword="$4"
while true; do
read -p "Install (${yesword} / ${noword})? " yn
if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi
if [[ "$yn" =~ $noexpr ]]; then exit; fi
echo "Answer ${yesword} / ${noword}."
done
Ответ 2
Есть, по крайней мере, пять вариантов ответов на этот вопрос.
Варианты:
Posix совместимый: может работать на старых системах с универсальной оболочкой
Использовать так называемые башизмы
Простые встроенные вопросы/ответы (типовые решения)
Красивые форматированные интерфейсы, например, ncurses или графические, используя libgtk или libqt ...
Использовать мощные возможности истории readline
1. Общие решения POSIX
Вы можете использовать команду read, за которой следует if ... then ... else:
echo -n "Хороший ли это вопрос (y/n)? "
read answer
if [ "$answer" != "${answer#[Yy]}" ] ;then
echo Yes
else
echo No
fi
2. POSIX, но с некоторой ключевой особенностью
Но если вы не хотите, чтобы пользователь нажимал клавишу Enter, вы можете написать:
echo -n " Хороший ли это вопрос (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # осторожно с stty
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
То же самое, но с явным ожиданием Y/N:
#/bin/sh
echo -n " Хороший ли это вопрос (y/n)? "
old_stty_cfg=$(stty -g)
stty raw -echo
answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )
stty $old_stty_cfg
if echo "$answer" | grep -iq "^y" ;then
echo Yes
else
echo No
fi
Использование специальных инструментов
Есть много инструментов , которые были собраны с использованием libncurses, libgtk, libqt или с другими графическими библиотеками. Например, используя whiptail:
if whiptail --yesno " Хороший ли это вопрос " 20 60 ;then
echo Yes
else
echo No
fi
В зависимости от вашей системы вам может потребоваться заменить whiptail на другой аналогичный инструмент:
dialog --yesno " Хороший ли это вопрос " 20 60 && echo Yes
gdialog --yesno " Хороший ли это вопрос " 20 60 && echo Yes
kdialog --yesno " Хороший ли это вопрос " 20 60 && echo Yes
где 20— высота диалогового окна в строках, а 60— ширина диалогового окна. Все эти инструменты имеют примерно одинаковый синтаксис.
DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...
3. Решения, специфичные для Bash
Базовый метод:
read -p " Хороший ли это вопрос (y/n)? " answer
case ${answer:0:1} in
y|Y )
echo Yes
;;
* )
echo No
;;
esac
Я предпочитаю использовать case,чтобы yes | ja | si | oui при необходимости можно было протестировать ...
В bash мы можем указать длину предполагаемого ввода для команды read:
read -n 1 -p " Хороший ли это вопрос (y/n)? " answer
В bash команда read принимает параметр тайм-аута, который может быть полезен.
read -t 3 -n 1 -p " Хороший ли это вопрос (y/n)? " answer
[ -z "$answer" ] && answer="Yes" # да, это выбор по умолчанию
4. Некоторые хитрости для специальных инструментов
Более сложные диалоговые окна, выходящие за рамки yes – no выбора:
dialog --menu "Хороший ли это вопрос" 20 60 12 y Yes n No m Maybe
Индикатор:
dialog --gauge "Заполнение индикатора" 20 60 0 < <(
for i in {1..100};do
printf "XXX\n%d\n%(%a %b %T)T индикатор: %d\nXXX\n" $i -1 $i
sleep .033
done
)
Маленькая демонстрация:
#!/bin/sh
while true ;do
[ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog
DIALOG=$($DIALOG --menu "Выберите инструмент для запуска" 20 60 12 2>&1 \
whiptail "диалог для оболочки скрипта >/dev/tty \
dialog "диалог для оболочки ncurses" \
gdialog "диалог для оболочки Gtk" \
kdialog "диалог для оболочки Kde" ) || exit
clear;echo "Выбрано: $DIALOG."
for i in `seq 1 100`;do
date +"`printf "XXX\n%d\n%%a %%b %%T индикатор: %d\nXXX\n" $i $i`"
sleep .0125
done | $DIALOG --gauge "Заполнение индикатора" 20 60 0
$DIALOG --infobox "Это простое информационное сообщение\n\n\nНе требуется никаких действий " 20 60
sleep 3
if $DIALOG --yesno "Нравится ли вам эта демонстрация?" 20 60 ;then
AnsYesNo=Yes; else AnsYesNo=No; fi
AnsInput=$($DIALOG --inputbox "Текст:" 20 60 "Текст здесь..." 2>&1 >/dev/tty)
AnsPass=$($DIALOG --passwordbox "Пароль:" 20 60 "..." 2>&1 >/dev/tty)
$DIALOG --textbox /etc/motd 20 60
AnsCkLst=$($DIALOG --checklist "Проверка..." 20 60 12 \
Correct "Успех" off \
Fun "Нормально" off \
Strong "В комплексе" on 2>&1 >/dev/tty)
AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12 \
" -1" "Понизить рейтинг этого ответа " off \
" 0" "Ничего не делать " on \
" +1" "Обновить этот ответ " off 2>&1 >/dev/tty)
out="Your answers:\nLike: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass"
$DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60
done
5. Использование истории readline
Пример:
#!/bin/bash
set -i
HISTFILE=~/.myscript.history
history -c
history -r
myread() {
read -e -p '> ' $1
history -s ${!1}
}
trap 'history -a;exit' 0 1 2 3 6
while myread line;do
case ${line%% *} in
exit ) break ;;
* ) echo "Сделать что-то с '$line'" ;;
esac
done
Это создаст файл .myscript.history в вашем каталоге $HOME, после чего вы сможете использовать команды истории readline, такие как Up, Down, Ctrl+r и другие.
Ответ 3
Вы можете использовать встроенную команду read. Используйте опцию -p, чтобы задать пользователю вопрос. Начиная с BASH4, вы можете использовать опцию -i, чтобы предложить ответ:
read -e -p "Введите путь к файлу: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH
(Но не забудьте использовать опцию «readline», -e,чтобы разрешить редактирование строки с помощью клавиш со стрелками).
Если вам нужна логика «да/нет», вы можете сделать что-то вроде этого:
read -e -p "
Перечислите содержимое вашей корневой папки [Y/n] " YN
[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
Ответ 4
inquire () {
echo -n "$1 [y/n]? "
read answer
finish="-1"
while [ "$finish" = '-1' ]
do
finish="1"
if [ "$answer" = '' ];
then
answer=""
else
case $answer in
y | Y | yes | YES ) answer="y";;
n | N | no | NO ) answer="n";;
*) finish="-1";
echo -n 'Неверный ответ – попробуйте снова:';
read answer;;
esac
fi
done
}
...
inquire "Установить сейчас?"
...
Ответ 5
В оболочке POSIX можно обрабатывать «выбор Да/Нет» с учетом локали; используя записи категории LC_MESSAGES локали, witch предоставляет готовые шаблоны RegEx для соответствия входу строки для локализованных Да/Нет.
#!/usr/bin/env sh
# Получение значений LC_MESSAGES в переменные
# shellcheck disable=SC2046 # Намеренное разделение IFS
IFS='
' set -- $(locale LC_MESSAGES)
yesexpr="$1"
noexpr="$2"
yesstr="$3"
nostr="$4"
messages_codeset="$5" # здесь не используется, но сохраняется как документация
# Отображение запроса "Да/Нет?" в локали
echo "$yesstr / $nostr ?"
# Чтение ответа
read -r yn
# Тестирование ответа
case "$yn" in
# работать только с классом символов из выражения
${yesexpr##^}) echo "ответ $yesstr" ;;
${noexpr##^}) echo "ответ $nostr" ;;
esac
Linux