如何在Linux shell脚本中提示是/否/取消input?

我想在shell脚本中暂停input,并提示用户select。 标准的“是,否,或取消”types的问题。 我如何在典型的bash提示符下完成这个任务?

read命令是在shell提示下获取用户input的最简单和最广泛的方法。 说明其使用的最佳方式是一个简单的示例:

 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 

另一种方法,史蒂文Huwig指出,是Bash的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您不需要清理input – 它显示可用选项,并键入与您的select相对应的数字。 它也会自动循环,所以如果给出无效的input,就不需要一个while true循环来重试。

此外,请查看F. Hauri的优秀答案 。

 echo "Please enter some input: " read input_variable echo "You entered: $input_variable" 

一个通用问题至less有五个答案。

取决于

  • 符合posix :可以在通用的shell环境下运行在较差的系统上
  • bash具体:使用所谓的bashisms

如果你想

  • 简单的“在线''问题/答案(通用解决scheme)
  • 相当格式化的接口,如ncurses或更多的graphics使用libgtk或libqt …
  • 使用强大的readline历史loggingfunction

1. POSIX通用解决scheme

你可以使用read命令,其次是if ... then ... else

 echo -n "Is this a good question (y/n)? " read answer if echo "$answer" | grep -iq "^y" ;then echo Yes else echo No fi 

POSIX,但单键function

但是,如果你不想让用户必须打回车 ,你可以写:

编辑:正如@JonathanLeffler正确地build议, 保存 stty的configuration可能比简单地强制他们理智 。)

 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下testing的 !

同样,但是明确地等待yn

 #/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 

使用专用工具

有许多使用libncurseslibgtklibqt或其他graphics库构build的工具。 例如,使用whiptail

 if whiptail --yesno "Is this a good question" 20 60 ;then echo Yes else echo No fi 

根据您的系统,您可能需要用另一个siliar工具replacewhiptail

 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/dialog ] ;then DIALOG=gdialog ; fi if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi ... $DIALOG --yesno ... 

2.打击具体的解决scheme

基本的在线方法

 read -p "Is this a good question (y/n)? " answer case ${answer:0:1} in y|Y ) echo Yes ;; * ) echo No ;; esac 

我更喜欢使用case所以我甚至可以testingyes | ja | si | oui yes | ja | si | oui yes | ja | si | oui如果需要…

符合 单键function

在bash下,我们可以为read命令指定预期input的长度:

 read -n 1 -p "Is this a good question (y/n)? " answer 

在bash下, read命令接受一个超时参数,这可能是有用的。

 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 

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 "Doing something with '$line'" ;; esac done 

这将在你的$HOME目录中创build一个.myscript.history文件,比你使用readline的历史命令,比如UpDownCtrl + r等等。

您可以使用内置的读取命令。 使用-p选项来提示用户一个问题。

既然BASH4,你现在可以使用-i来build议一个答案,所以用户只需要按return可以进入:

 read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH echo $FILEPATH 

(但请记住使用“readline”选项-e允许使用箭头键进行行编辑)

Bash已经select了这个目的。

 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 "$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 

我是一个初学者,所以拿一点盐,但它似乎工作。

你要:

  • Bash内置命令(即便携式)
  • 检查TTY
  • 默认答案
  • 时间到
  • 彩色的问题

片段

 do_xxxx=y # In batch mode => Default is Yes [[ -t 0 ]] && # If tty => prompt the question read -t 5 -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', 'Y' or empty then xxxx fi 

说明

  • [[ -t 0 ]] && read ... =>只能在TTY中读取呼叫
  • read -t 5 =>超时5秒
  • read -n 1 =>等待一个字符
  • $'\e[1;32m ... \e[0m ' =>以绿色打印
    (在白色/黑色背景上可读)
    • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash正则expression式

超时=>默认答案是否

 do_xxxx=y [[ -t 0 ]] && { 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 
 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?" ... 

用最less的行数实现这个最简单的方法如下:

 read -p "<Your Friendly Message here> : y/n/cancel" CONDITION; if [ "$CONDITION" == "y" ]; then # do something here! fi 

if只是一个例子:如何处理这个variables取决于你。

使用read命令:

 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 

然后你需要的所有其他东西

此解决scheme读取单个字符并在yes响应中调用函数。

 read -p "Are you sure? (y/n) " -n 1 echo if [[ $REPLY =~ ^[Yy]$ ]]; then do_something fi 
 read -e -p "Enter your choice: " choice 

-e选项使用户能够使用箭头键编辑input。

如果你想使用一个build议作为input:

 read -e -i "yes" -p "Enter your choice: " choice 

-i选项打印一个暗示input。

对不起,张贴在这样一个旧的职位。 几个星期前,我面临着类似的问题,在我的情况下,我需要一个解决scheme,也可以在一个在线安装脚本,例如: curl -Ss https://raw.github.com/_____/installer.sh | bash 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的input框,使用如下命令对话框

 #!/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 

至less在SUSE Linux中默认安装对话框。

多选版本:

 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 '\n')" && printf '\n' ( IFS='|' for o in $2; 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的回答启发,我为通用提示创build了这个函数

 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\n" done } 

像这样使用它:

 unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "axf" selection echo "$selection" 

一个简单的方法是使用xargs -p或gnu parallel --interactive

我喜欢xargs的行为,因为它像其他交互式unix命令一样,在提示符之后立即执行每个命令,而不是收集yesses在最后运行。 (你可以通过你想要的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 } 

我build议你使用对话框 …

Linux学徒:使用对话框改进Bash Shell脚本

对话框命令允许在shell脚本中使用窗口框以使其更加交互。

它简单易用,还有一个名为gdialog的gnome版本,它使用完全相同的参数,但在X上显示GUI风格。

作为单行命令的朋友,我使用了以下内容:

 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=; 

我注意到,没有人发布了一个答案,显示这样简单的用户input的多行回声菜单,所以这里是我去的地方:

 #!/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 

这种方法是希望有人发现它有用和省时。

 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!' 

在这种情况下,我已经多次使用case语句,使用case语句是一个好方法。 一个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, eg /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 

用干净的用户input确认

function:

 @confirm() { local message="$1" echo -n "> $message (y/n) " while true ; do read -s -n 1 choice case "$choice" in y|Y ) echo "Y" ; return 0 ;; n|N ) echo "N" ; return 1 ;; esac done } 

用法:

 if @confirm 'Confirm?' ; then echo "Yes" else echo "No" fi 

php ,这对于shell脚本来说非常强大:

 #!/usr/bin/php <?php $yo = readline("Do you wish to install this program? [Y/y] or [ctrl+c to quit]"); if ($yo == "Y" || $yo == "y") { echo "Installing.."; system("sudo apt install ..."); } 

php-cli包只对这个是强制性的。