27 ответов:
самый простой и наиболее широко доступный способ получить пользовательский ввод в командной строке-это
read. Лучший способ проиллюстрировать его использование-это простая демонстрация:while true; do read -p "Do you wish to install this program?" yn case $yn in [Yy]* ) make install; break;; [Nn]* ) exit;; * ) echo "Please answer yes or no.";; esac doneеще один метод, на который указал Стивен Хьювиг, - это Bash's
select. Вот такой же пример использованияselect:echo "Do you wish to install this program?" select yn in "Yes" "No"; do case $yn in Yes ) make install; break;; No ) exit;; esac doneС
selectвам не нужно санировать вход – он отображает доступные варианты, и вы вводите номер соответствует вашему выбору. Он также петли автоматически, так что нет необходимости вwhile trueцикл для повторной попытки, если они дают неверный ввод.кроме того, пожалуйста, проверьте отличный ответ!--9--> Ф. Hauri.
по крайней мере пять ответов на один общий вопрос.
в зависимости от
- posix совместимый: может работать на плохих системах с generic shell окружающая среда
- Баш specific: использование так называемых bashisms
и если вы хотите
- простой "в строке" вопрос / ответ (общие решения)
- довольно отформатирован интерфейсы, как ncurses или более графический с помощью libgtk или libqt...
- используйте мощную возможность истории readline
1. POSIX generic solutions
вы могли бы использовать
readкоманда, за которой следуетif ... then ... else:echo -n "Is this a good question (y/n)? " read answer
# if echo "$answer" | grep -iq "^y" ;thenif [ "$answer" != "${answer#[Yy]}" ] ;then echo Yes else echo No fi(спасибо комментарий Адама Каца: заменил тест выше на тот, который является более портативным и избегает одной вилки:)
POSIX, но одна ключевая особенность
но если вы не хотите, чтобы пользователь ударил возвращение, вы могли бы написать:
(редактировать: как справедливо предположил @JonathanLeffler,экономия конфигурация stty может быть лучше, чем просто заставить их вменяемого.)
echo -n "Is this a good question (y/n)? " old_stty_cfg=$(stty -g) stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty if echo "$answer" | grep -iq "^y" ;then echo Yes else echo No fiПримечание: это было проверено под ш,Баш, КШ,тире и busybox!
то же самое, но явно ждет y или n:
#/bin/sh echo -n "Is this a good question (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 "Is this a good question" 20 60 ;then echo Yes else echo No fiв зависимости от вашей системы, вам может понадобиться, чтобы заменить
whiptailС другой аналогичный инструмент:dialog --yesno "Is this a good question" 20 60 && echo Yes gdialog --yesno "Is this a good question" 20 60 && echo Yes kdialog --yesno "Is this a good question" 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 ...2. Баш конкретные решения
Basic в строке метод
read -p "Is this a good question (y/n)? " answer case ${answer:0:1} in y|Y ) echo Yes ;; * ) echo No ;; esacя предпочитаю использовать
caseтак что я мог бы даже проверить дляyes | ja | si | ouiпри необходимости...в строке С один ключ особенность
в разделе bash мы можем указать длину предполагаемого ввода для:
read -n 1 -p "Is this a good question (y/n)? " answerпод Баш, принимает автоотключение параметр, который может быть полезен.
read -t 3 -n 1 -p "Is this a good question (y/n)? " answer [ -z "$answer" ] && answer="Yes" # if 'yes' have to be default choiceнекоторые хитрости для специальные инструменты
более сложные диалоговые окна, а не только
yes - noцели:dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybeпрогресс бар:
dialog --gauge "Filling the tank" 20 60 0 < <( for i in {1..100};do printf "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $i -1 $i sleep .033 done )маленькое демо:
#!/bin/sh while true ;do [ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog DIALOG=$($DIALOG --menu "Which tool for next run?" 20 60 12 2>&1 \ whiptail "dialog boxes from shell scripts" >/dev/tty \ dialog "dialog boxes from shell with ncurses" \ gdialog "dialog boxes from shell with Gtk" \ kdialog "dialog boxes from shell with Kde" ) || exit clear;echo "Choosed: $DIALOG." for i in `seq 1 100`;do date +"`printf "XXX\n%d\n%%a %%b %%T progress: %d\nXXX\n" $i $i`" sleep .0125 done | $DIALOG --gauge "Filling the tank" 20 60 0 $DIALOG --infobox "This is a simple info box\n\nNo action required" 20 60 sleep 3 if $DIALOG --yesno "Do you like this demo?" 20 60 ;then AnsYesNo=Yes; else AnsYesNo=No; fi AnsInput=$($DIALOG --inputbox "A text:" 20 60 "Text here..." 2>&1 >/dev/tty) AnsPass=$($DIALOG --passwordbox "A secret:" 20 60 "First..." 2>&1 >/dev/tty) $DIALOG --textbox /etc/motd 20 60 AnsCkLst=$($DIALOG --checklist "Check some..." 20 60 12 \ Correct "This demo is useful" off \ Fun "This demo is nice" off \ Strong "This demo is complex" on 2>&1 >/dev/tty) AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12 \ " -1" "Downgrade this answer" off \ " 0" "Not do anything" on \ " +1" "Upgrade this anser" off 2>&1 >/dev/tty) out="Your answers:\nLike: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass" $DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60 doneбольше образец? Взгляните на используя whiptail для выбора устройства USB и USB съемный селектор хранения: USBKeyChooser
5. Используя историю readline
пример:
#!/bin/bash set -i HISTFILE=~/.myscript.history history -c history -r myread() { read -e -p '> ' history -s ${!1} } trap 'history -a;exit' 0 1 2 3 6 while myread line;do case ${line%% *} in exit ) break ;; * ) echo "Doing something with '$line'" ;; esac doneэто создаст файл
.myscript.historyв своем$HOMEкаталог, чем вы могли бы использовать команды истории readline, например до, вниз,Ctrl+r и другие.
вы можете использовать встроенный читать используйте
-pвозможность предложить пользователю с вопросом.начиная с BASH4, теперь вы можете использовать
-iчтобы предложить ответ, поэтому пользователю нужно только нажатьreturnчтобы войти в него:read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH echo $FILEPATH(но не забудьте использовать опцию "readline"
-eчтобы разрешить редактирование с помощью клавиш со стрелками)если вы хотите логику "да / нет", вы можете сделать что-то вроде этого:
read -e -p " List the content of your home dir ? [Y/n] " YN [[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
Баш выберите для этой цели.
select result in Yes No Cancel do echo $result done
read -p "Are you alright? (y/n) " RESP if [ "$RESP" = "y" ]; then echo "Glad to hear it" else echo "You need more bash programming" fi
вот что я собрал:
#!/bin/sh promptyn () { while true; do read -p " " yn case $yn in [Yy]* ) return 0;; [Nn]* ) return 1;; * ) echo "Please answer yes or no.";; esac done } if promptyn "is the sky blue?"; then echo "yes" else echo "no" fiЯ новичок, так что возьмите это с зерном соли, но это, кажется, работает.
inquire () { echo -n " [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 'Invalid response -- please reenter:'; read answer;; esac fi done } ... other stuff inquire "Install now?" ...
вы хотите:
- Bash встроенные команды (т. е. портативный)
- Проверьте TTY
- ответ по умолчанию
- автоотключение
- цвета вопрос
фрагмент
do_xxxx=y # In batch mode => Default is Yes [[ -t 0 ]] && # If TTY => Prompt the question read -n 1 -p $'\e[1;32m Do xxxx? (Y/n)\e[0m ' do_xxxx # Store the answer in $do_xxxx if [[ $do_xxxx =~ ^(y|Y|)$ ]] # Do if 'y' or 'Y' or empty then xxxx fiобъяснениями
[[ -t 0 ]] && read ...=> вызова командыreadЕсли TTYread -n 1=> подождите один символ$'\e[1;32m ... \e[0m '=> печать зеленым цветом
(зеленый цвет в порядке, потому что читается на оба белых / черных фона)[[ $do_xxxx =~ ^(y|Y|)$ ]]= > bash regexTimeout = > по умолчанию ответ-Нет
do_xxxx=y [[ -t 0 ]] && { # Timeout 5 seconds (read -t 5) read -t 5 -n 1 -p $'\e[1;32m Do xxxx? (Y/n)\e[0m ' do_xxxx || # read 'fails' on timeout do_xxxx=n ; } # Timeout => answer No if [[ $do_xxxx =~ ^(y|Y|)$ ]] then xxxx fi
самый простой способ добиться этого с наименьшим количеством строк выглядит следующим образом:
read -p "<Your Friendly Message here> : y/n/cancel" CONDITION; if [ "$CONDITION" == "y" ]; then # do something here! fiThe
ifэто просто пример: это до вас, как обрабатывать эту переменную.
использовать :
echo Would you like to install? "(Y or N)" read x # now check if $x is "y" if [ "$x" = "y" ]; then # do something here! fiа потом все остальные вещи, которые вам нужны
это решение считывает один символ и вызывает функцию на ответ да.
read -p "Are you sure? (y/n) " -n 1 echo if [[ $REPLY =~ ^[Yy]$ ]]; then do_something fi
read -e -p "Enter your choice: " choiceThe
-eопция позволяет пользователю редактировать ввод с помощью клавиш со стрелками.если вы хотите использовать предложение в качестве входных данных:
read -e -i "yes" -p "Enter your choice: " choice
-iопция печатает наводящий ввод.
извините за публикацию на такой старый пост. Несколько недель назад я столкнулся с подобной проблемой, в моем случае мне нужно было решение, которое также работало в онлайн-скрипте установки, например:
curl -Ss https://raw.github.com/_____/installer.sh | bashиспользуя
read yesno < /dev/ttyотлично работает для меня:echo -n "These files will be uploaded. Is this ok? (y/n) " read yesno < /dev/tty if [ "x$yesno" = "xy" ];then # Yes else # No fiнадеюсь, это кому-то поможет.
чтобы получить хороший ncurses-like inputbox используйте команду такой:
#!/bin/bash if (dialog --title "Message" --yesno "Want to do something risky?" 6 25) # message box will have the size 25x6 characters then echo "Let's do something risky" # do something risky else echo "Let's stay boring" fiпакет диалогового окна установлен по умолчанию, по крайней мере, с SUSE Linux.
множественный выбор версия:
ask () { # =question =options # set REPLY # options: x=..|y=.. while $(true); do printf '%s [%s] ' "" "" stty cbreak REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null) stty -cbreak test "$REPLY" != "$(printf '\n')" && printf '\n' ( IFS='|' for o in ; do if [ "$REPLY" = "${o%%=*}" ]; then printf '\n' break fi done ) | grep ^ > /dev/null && return done }пример:
$ ask 'continue?' 'y=yes|n=no|m=maybe' continue? [y=yes|n=no|m=maybe] g continue? [y=yes|n=no|m=maybe] k continue? [y=yes|n=no|m=maybe] y $Он будет ставить
REPLYдоy(внутри скрипта).
вдохновленный ответами @Mark и @Myrddin я создал эту функцию для универсального приглашения
uniprompt(){ while true; do echo -e "\c" read opt array=() case "${array[@]}" in *"$opt"*) eval "=$opt";return 0;; esac echo -e "$opt is not a correct value\n" done }используйте его так:
unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection echo "$selection"
Я предлагаю вам окне использовать...
Linux Apprentice: Улучшение Скриптов Оболочки Bash С Помощью Dialog
диалоговая команда позволяет использовать окна в сценариях оболочки, чтобы сделать их использование более интерактивным.
Он прост и удобен в использовании, есть также версия gnome под названием gdialog, которая принимает точно такие же параметры, но показывает его стиль GUI на X.
один простой способ сделать это с
xargs -pили gnuparallel --interactive.мне нравится поведение xargs немного лучше для этого, потому что он выполняет каждую команду сразу после приглашения, как и другие интерактивные команды unix, а не собирает yess для запуска в конце. (Вы можете Ctrl-C после того, как вы получите через те, которые вы хотели.)
например,
echo *.xml | xargs -p -n 1 -J {} mv {} backup/
более общим будет такой:
function menu(){ title="Question time" prompt="Select:" options=("Yes" "No" "Maybe") echo "$title" PS3="$prompt" select opt in "${options[@]}" "Quit/Cancel"; do case "$REPLY" in 1 ) echo "You picked $opt which is option $REPLY";; 2 ) echo "You picked $opt which is option $REPLY";; 3 ) echo "You picked $opt which is option $REPLY";; $(( ${#options[@]}+1 )) ) clear; echo "Goodbye!"; exit;; *) echo "Invalid option. Try another one.";continue;; esac done return }
только одно нажатие клавиши
вот более длинный, но многоразовый и модульный подход:
- возвращает
0=да1=не- не требуется нажимать enter - только один символ
- нажать введите принять выбор по умолчанию
- можно отключить выбор по умолчанию, чтобы заставить выбор
- работы как
zshиbash.по умолчанию "нет", когда нажатие enter
отметим, что
NС большой буквы. Здесь нажать enter, по умолчанию:$ confirm "Show dangerous command" && echo "rm *" Show dangerous command [y/N]?также обратите внимание, что
[y/N]?был автоматически добавлен. По умолчанию "нет" принимается, поэтому ничего не повторяется.повторите запрос, пока не будет дан правильный ответ:
$ confirm "Show dangerous command" && echo "rm *" Show dangerous command [y/N]? X Show dangerous command [y/N]? y rm *по умолчанию "да" при нажатии Enter
отметим, что
YС большой буквы:$ confirm_yes "Show dangerous command" && echo "rm *" Show dangerous command [Y/n]? rm *выше, Я просто нажал enter, так что команда побежала.
нет по умолчанию на введите требуют
yилиn$ get_yes_keypress "Here you cannot press enter. Do you like this [y/n]? " Here you cannot press enter. Do you like this [y/n]? k Here you cannot press enter. Do you like this [y/n]? Here you cannot press enter. Do you like this [y/n]? n $ echo $? 1здесь
1или false был возвращен. Обратите внимание, что с помощью этой функции нижнего уровня вам нужно будет предоставить свой собственный[y/n]?запрос.код
# Read a single char from /dev/tty, prompting with "$*" # Note: pressing enter will return a null string. Perhaps a version terminated with X and then remove it in caller? # See https://unix.stackexchange.com/a/367880/143394 for dealing with multi-byte, etc. function get_keypress { local REPLY IFS= >/dev/tty printf '%s' "$*" [[ $ZSH_VERSION ]] && read -rk1 # Use -u0 to read from STDIN # See https://unix.stackexchange.com/q/383197/143394 regarding '\n' -> '' [[ $BASH_VERSION ]] && </dev/tty read -rn1 printf '%s' "$REPLY" } # Get a y/n from the user, return yes=0, no=1 enter= # Prompt using . # If set, return on pressing enter, useful for cancel or defualting function get_yes_keypress { local prompt="${1:-Are you sure [y/n]? }" local enter_return= local REPLY # [[ ! $prompt ]] && prompt="[y/n]? " while REPLY=$(get_keypress "$prompt"); do [[ $REPLY ]] && printf '\n' # $REPLY blank if user presses enter case "$REPLY" in Y|y) return 0;; N|n) return 1;; '') [[ $enter_return ]] && return "$enter_return" esac done } # Credit: http://unix.stackexchange.com/a/14444/143394 # Prompt to confirm, defaulting to NO on <enter> # Usage: confirm "Dangerous. Are you sure?" && rm * function confirm { local prompt="${*:-Are you sure} [y/N]? " get_yes_keypress "$prompt" 1 } # Prompt to confirm, defaulting to YES on <enter> function confirm_yes { local prompt="${*:-Are you sure} [Y/n]? " get_yes_keypress "$prompt" 0 }
yn() { if [[ 'y' == `read -s -n 1 -p "[y/n]: " Y; echo $Y` ]]; then eval ; else eval ; fi } yn 'echo yes' 'echo no' yn 'echo absent no function works too!'
в качестве друга команды одной строки я использовал следующее:
while [ -z $prompt ]; do read -p "Continue (y/n)?" choice;case "$choice" in y|Y ) prompt=true; break;; n|N ) exit 0;; esac; done; prompt=;написано longform, он работает следующим образом:
while [ -z $prompt ]; do read -p "Continue (y/n)?" choice; case "$choice" in y|Y ) prompt=true; break;; n|N ) exit 0;; esac; done; prompt=;
я использовал
caseзаявление пару раз в таком сценарии, используя case statment является хорошим способом, чтобы пойти об этом. Аwhileцикл, который экапсулируетcaseблок, который использует логическое условие, может быть реализован для того, чтобы еще больше контролировать программу и выполнять многие другие требования. После того, как все условия были соблюдены,breakможет использоваться, который передаст управление обратно в основную часть программы. Кроме того, для удовлетворения других условиях, условные операторы курса могут быть добавлены, чтобы сопровождать структуры управления:caseзаявление и возможноwhileпетли.пример использования
caseзаявление для выполнения вашего запроса#! /bin/sh # For potential users of BSD, or other systems who do not # have a bash binary located in /bin the script will be directed to # a bourne-shell, e.g. /bin/sh # NOTE: It would seem best for handling user entry errors or # exceptions, to put the decision required by the input # of the prompt in a case statement (case control structure), echo Would you like us to perform the option: "(Y|N)" read inPut case $inPut in # echoing a command encapsulated by # backticks (``) executes the command "Y") echo `Do something crazy` ;; # depending on the scenario, execute the other option # or leave as default "N") echo `execute another option` ;; esac exit
Я заметил, что никто не опубликовал ответ, показывающий многострочное меню echo для такого простого пользовательского ввода, поэтому вот мой ответ:
#!/bin/bash function ask_user() { echo -e " #~~~~~~~~~~~~# | 1.) Yes | | 2.) No | | 3.) Quit | #~~~~~~~~~~~~#\n" read -e -p "Select 1: " choice if [ "$choice" == "1" ]; then do_something elif [ "$choice" == "2" ]; then do_something_else elif [ "$choice" == "3" ]; then clear && exit 0 else echo "Please select 1, 2, or 3." && sleep 3 clear && ask_user fi } ask_userэтот метод был опубликован в надежде, что кто-то может найти это полезным и сэкономить время.
Да / Нет / Отмена
функции
#!/usr/bin/env bash @confirm() { local message="$*" local result='' echo -n "> $message (Yes/No/Cancel) " >&2 while [ -z "$result" ] ; do read -s -n 1 choice case "$choice" in y|Y ) result='Y' ;; n|N ) result='N' ;; c|C ) result='C' ;; esac done echo $result }использование
case $(@confirm 'Confirm?') in Y ) echo "Yes" ;; N ) echo "No" ;; C ) echo "Cancel" ;; esacподтвердите с чистым пользовательским вводом
функции
#!/usr/bin/env bash @confirm() { local message="$*" local result=3 echo -n "> $message (y/n) " >&2 while [[ $result -gt 1 ]] ; do read -s -n 1 choice case "$choice" in y|Y ) result=0 ;; n|N ) result=1 ;; esac done return $result }использование
if @confirm 'Confirm?' ; then echo "Yes" else echo "No" fi
в ответ на другие:
вам не нужно указывать регистр в BASH4, просто используйте',,', чтобы сделать нижний регистр var. Также мне очень не нравится помещать код внутри блока чтения, получать результат и иметь дело с ним за пределами блока чтения IMO. Также включите " q " для выхода из IMO. Наконец, почему введите " да " просто используйте -n1 и нажмите y.
пример: пользователь может нажать y / n, а также q, чтобы просто выйти.
ans='' while true; do read -p "So is MikeQ the greatest or what (y/n/q) ?" -n1 ans case ${ans,,} in y|n|q) break;; *) echo "Answer y for yes / n for no or q for quit.";; esac done echo -e "\nAnswer = $ans" if [[ "${ans,,}" == "q" ]] ; then echo "OK Quitting, we will assume that he is" exit 0 fi if [[ "${ans,,}" == "y" ]] ; then echo "MikeQ is the greatest!!" else echo "No? MikeQ is not the greatest?" fi
Comments