How do I prompt for Yes/No/Cancel input in a Linux shell script?
我想暂停shell脚本中的输入,并提示用户选择。标准的"是、否或取消"类型的问题。如何在典型的bash提示中完成这一点?
在shell提示下获取用户输入的最简单和最广泛可用的方法是
1 2 3 4 5 6 7 8 | 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 |
StevenHuwig指出的另一种方法是bash的
1 2 3 4 5 6 7 | 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 |
使用
另外,请查看F.Hauri的完美答案。
一个普通问题至少有五个答案。
取决于
- 符合POSIX:可以在具有通用shell环境的差系统上工作
- bash-specific:使用所谓的bashims
如果你想要
- 简单的"在线"问题/答案(通用解决方案)
- 使用libgtk或libqt的漂亮格式接口,如ncurses或更图形化的接口…
- 使用强大的阅读线历史记录功能
1。POSIX通用解决方案
您可以使用
1 2 | echo -n"Is this a good question (y/n)?" read answer |
1 | # if echo"$answer" | grep -iq"^y" ;then |
1 2 3 4 5 | if ["$answer" !="${answer#[Yy]}" ] ;then echo Yes else echo No fi |
(多亏了亚当·卡茨的评论:用一个更便携、避免使用一个分叉的测试替换了上面的测试。)
POSIX,但单键功能但是如果您不希望用户必须点击return,您可以写:
(编辑:正如@jonathanlefler正确地建议的那样,保存stty的配置可能比简单地强迫他们保持清醒要好。)
1 2 3 4 5 6 7 8 | 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 |
注意:这是在sh、bash、ksh、dash和busybox下测试的!
相同,但明确等待y或n:
1 2 3 4 5 6 7 8 9 10 11 | #/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 |
使用专用工具
有许多工具是使用
1 2 3 4 5 | if whiptail --yesno"Is this a good question" 20 60 ;then echo Yes else echo No fi |
根据您的系统,您可能需要用另一个类似的工具替换
1 2 3 4 5 | 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 |
其中,
1 2 3 4 5 | DIALOG=whiptail if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi ... $DIALOG --yesno ... |
2。bash特定的解决方案基本直线法
1 2 3 4 5 6 7 8 9 | read -p"Is this a good question (y/n)?" answer case ${answer:0:1} in y|Y ) echo Yes ;; * ) echo No ;; esac |
我更喜欢使用
在bash下,我们可以为
1 | read -n 1 -p"Is this a good question (y/n)?" answer |
在bash下,
1 2 | 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 |
专用工具的一些技巧
除了简单的
1 | dialog --menu"Is this a good question" 20 60 12 y Yes n No m Maybe |
进度条:
1 2 3 4 5 6 7 8 9 10 | dialog --gauge"Filling the tank" 20 60 0 < <( for i in {1..100};do printf"XXX\ %d\ %(%a %b %T)T progress: %d\ XXX\ " $i -1 $i sleep .033 done ) |
小演示:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | #!/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\ %d\ %%a %%b %%T progress: %d\ XXX\ " $i $i`" sleep .0125 done | $DIALOG --gauge"Filling the tank" 20 60 0 $DIALOG --infobox"This is a simple info box\ \ No 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:\ Like: $AnsYesNo\ Input: $AnsInput\ Secret: $AnsPass" $DIALOG --msgbox"$out\ Attribs: $AnsCkLst\ Note: $AnsRadio" 20 60 done |
更多样本?看看使用whiptail选择USB设备和USB可移动存储选择器:usbkeycooser
5。使用readline的历史记录例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | #!/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"Doing something with '$line'" ;; esac done |
这将在您的
1 2 3 | echo"Please enter some input:" read input_variable echo"You entered: $input_variable" |
您可以使用内置的read命令;使用
由于bash4,现在可以使用
1 2 | read -e -p"Enter the path to the file:" -i"/usr/local/etc/" FILEPATH echo $FILEPATH |
(但记住使用"readline"选项
如果需要"是/否"逻辑,可以这样做:
1 2 3 4 | read -e -p" List the content of your home dir ? [Y/n]" YN [[ $YN =="y" || $YN =="Y" || $YN =="" ]] && ls -la ~/ |
Bash为此选择了。
1 2 3 4 | select result in Yes No Cancel do echo $result done |
1 2 3 4 5 6 | 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 |
以下是我整理的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/bin/sh promptyn () { while true; do read -p"$1" 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 |
我是个初学者,所以拿着这个加一点盐,但它似乎有效。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | 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 'Invalid response -- please reenter:'; read answer;; esac fi done } ... other stuff inquire"Install now?" ... |
你想要:
- bash内置命令(即便携式)
- 检查TTY
- 默认答案
- 超时
- 彩色问题
片断
1 2 3 4 5 6 7 8 | 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 regex
超时=>默认答案为否
1 2 3 4 5 6 7 8 9 | 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 |
用最少的行数实现这一点的最简单方法如下:
1 2 3 4 5 | read -p"<Your Friendly Message here> : y/n/cancel" CONDITION; if ["$CONDITION" =="y" ]; then # do something here! fi |
使用
1 2 3 4 5 6 7 8 | 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 |
然后所有你需要的东西
此解决方案读取单个字符并在Yes响应上调用函数。
1 2 3 4 5 | read -p"Are you sure? (y/n)" -n 1 echo if [[ $REPLY =~ ^[Yy]$ ]]; then do_something fi |
1 | read -e -p"Enter your choice:" choice |
如果要使用建议作为输入:
1 | read -e -i"yes" -p"Enter your choice:" choice |
很抱歉在这么旧的帖子上发帖。几周前,我也遇到了类似的问题,在我的例子中,我需要一个在在线安装程序脚本中也有效的解决方案,例如:
使用
1 2 3 4 5 6 7 8 9 10 | 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 |
希望这能帮助别人。
要获得像inputbox这样的好的ncurse,请使用如下的命令对话框:
1 2 3 4 5 6 7 8 9 | #!/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上安装对话框包。
仅限单按键
这里有一个较长但可重用的模块化方法:
- 返回
0 是,1 否 - 不需要按Enter键-只需要一个字符
- 可以按enter接受默认选项
- 可以禁用默认选项以强制选择
- 适用于
zsh 和bash 。
按Enter时默认为"否"
注意,
1 2 | $ confirm"Show dangerous command" && echo"rm *" Show dangerous command [y/N]? |
另请注意,
重新提示直到给出有效响应:
1 2 3 4 | $ confirm"Show dangerous command" && echo"rm *" Show dangerous command [y/N]? X Show dangerous command [y/N]? y rm * |
按Enter时默认为"是"
注意,
1 2 3 | $ confirm_yes"Show dangerous command" && echo"rm *" Show dangerous command [Y/n]? rm * |
上面,我刚按下Enter,命令就运行了。
enter无违约-需要1 2 3 4 5 6 | $ 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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | # 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 '\ ' -> '' [[ $BASH_VERSION ]] && </dev/tty read -rn1 printf '%s'"$REPLY" } # Get a y/n from the user, return yes=0, no=1 enter=$2 # Prompt using $1. # If set, return $2 on pressing enter, useful for cancel or defualting function get_yes_keypress { local prompt="${1:-Are you sure [y/n]? }" local enter_return=$2 local REPLY # [[ ! $prompt ]] && prompt="[y/n]?" while REPLY=$(get_keypress"$prompt"); do [[ $REPLY ]] && printf '\ ' # $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 } |
您可以在
1 2 3 4 5 6 7 | read -rp"Do you want a demo? [y/n/c]" [[ ${REPLY,,} =~ ^(c|cancel)$ ]] && { echo"Selected Cancel"; exit 1; } if [[ ${REPLY,,} =~ ^(y|yes|j|ja|s|si|o|oui)$ ]]; then echo"Positive" fi |
多选版本:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | ask () { # $1=question $2=options # set REPLY # options: x=..|y=.. while $(true); do printf '%s [%s] '"$1""$2" stty cbreak REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null) stty -cbreak test"$REPLY" !="$(printf '\ ')" && printf '\ ' ( IFS='|' for o in $2; do if ["$REPLY" ="${o%%=*}" ]; then printf '\ ' break fi done ) | grep ^ > /dev/null && return done } |
例子:
1 2 3 4 5 | $ 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 $ |
它将把
我注意到,没有人发布一个答案,显示了这样简单的用户输入的多行回音菜单,所以下面是我的步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | #!/bin/bash function ask_user() { echo -e" #~~~~~~~~~~~~# | 1.) Yes | | 2.) No | | 3.) Quit | #~~~~~~~~~~~~#\ " 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 |
这个方法被贴出来,希望有人能发现它有用,节省时间。
受@mark和@myrddin答案的启发,我创建了一个通用提示的函数
1 2 3 4 5 6 7 8 9 10 | uniprompt(){ while true; do echo -e"$1\\c" read opt array=($2) case"${array[@]}" in *"$opt"*) eval"$3=$opt";return 0;; esac echo -e"$opt is not a correct value\ " done } |
这样使用:
1 2 | unipromtp"Select an option: (a)-Do one (x)->Do two (f)->Do three :""a x f" selection echo"$selection" |
一个简单的方法是使用
我对此比较喜欢xargs的行为,因为它像其他交互的unix命令一样在提示后立即执行每个命令,而不是收集在末尾运行的yes。(您可以在完成所需的任务后按ctrl-c。)
例如。,
1 | echo *.xml | xargs -p -n 1 -J {} mv {} backup/ |
更一般的是:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 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 } |
我建议你使用对话…
Linux Apprentice: Improve Bash Shell Scripts Using Dialog
The dialog command enables the use of window boxes in shell scripts to make their use more interactive.
它简单易用,还有一个GNOME版本叫做gDialog,它使用完全相同的参数,但在X上显示了它的GUI样式。
1 2 3 4 5 6 7 | yn() { if [[ 'y' == `read -s -n 1 -p"[y/n]:" Y; echo $Y` ]]; then eval $1; else eval $2; fi } yn 'echo yes' 'echo no' yn 'echo absent no function works too!' |
作为单行命令的朋友,我使用了以下命令:
1 | 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=; |
写的很长,它的工作原理如下:
1 2 3 4 5 6 7 8 | 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=; |
在这种情况下,我已经多次使用了
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | #! /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 |
回应他人:
您不需要在bash4中指定case,只需使用",,"将var变为小写。另外,我非常不喜欢把代码放在读取块内,得到结果并在读取块IMO之外处理它。还包括退出IMO的"q"。最后,为什么键入"yes"只使用-n1并按下y。
示例:用户可以按Y/N和Q退出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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"\ Answer = $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 |
是/否/取消功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | #!/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 } |
用法
1 2 3 4 5 | case $(@confirm 'Confirm?') in Y ) echo"Yes" ;; N ) echo"No" ;; C ) echo"Cancel" ;; esac |
用干净的用户输入确认功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | #!/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 } |
用法
1 2 3 4 5 | if @confirm 'Confirm?' ; then echo"Yes" else echo"No" fi |