Я хочу приостановить ввод в сценарии оболочки и предложить пользователю выбор.
Стандартный вопрос 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 ; then make install; exit; fi
if ; 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
&& 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