当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在Linuxshell脚本中提示是/否/取消输入?》经验,为你挑选了18个好方法。

我想在shell脚本中暂停输入,并提示用户进行选择.标准的"是,否或取消"类型问题.如何在典型的bash提示符中完成此操作?



1> Myrddin Emry..:

在shell提示符下获取用户输入的最简单且最广泛可用的方法是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

Steven 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你并不需要净化输入-它显示可用的选项,你键入相应的你的选择一个号码.它也会自动循环,因此while true如果它们提供无效输入,则无需重试循环.

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


在OS X Leopard中使用Bash,当我选择'no'时,我将`exit`改为`break`以防止关闭标签.
如果没有循环,为什么在“选择”中有一个“中断”?

2> F. Hauri..:
一个通用问题至少有五个答案.

取决于

符合posix:可以在具有通用shell环境的不良系统上工作

bash具体:使用所谓的bashisms

如果你想要的话

简单的``在线''问题/答案(通用解决方案)

相当格式化的接口,如使用libgtk或libqt的ncurses或更多图形...

使用强大的readline历史记录功能

1. POSIX通用解决方案

您可以使用该read命令,然后执行以下操作if ... then ... else:

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

# if echo "$answer" | grep -iq "^y" ;then

if [ "$answer" != "${answer#[Yy]}" ] ;then
    echo Yes
else
    echo No
fi

(感谢Adam Katz的评论:将上面的测试替换为一个更便携的测试并避免使用一个分叉:)

POSIX,但是单一的关键功能

但如果您不希望用户必须点击Return,您可以写:

(编辑:正如@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

注意:这是在sh,bash,ksh,dash和busybox下测试的!

相同,但明确等待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

使用专用工具

有很多其使用内置的工具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. Bash特定解决方案

基本的在线方法

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命令指定预期输入的长度:

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

更多样品?看看使用whiptail选择USB设备和USB可移动存储选择器:USBKeyChooser

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

这将创建一个文件.myscript.history$HOME目录中,比你能使用输入行的历史命令,比如Up, Down,Ctrl+ r等.


您可以将`case`用于POSIX以及bash(使用通配符条件而不是bash子字符串:`case $ answer in; [Yy]*)echo Yes ;;`),但我更喜欢使用条件语句,赞成`["$ answer"!="$ {answer#[Yy]}"]`超过你的'echo'$ answer"| grep -iq ^ y`.它更便携(一些非GNU greps没有正确实现`-q`)并且它没有系统调用.`$ {answer#[Yy]}`使用参数扩展从`$ answer`的开头删除`Y`或`y`,当存在任何一个时导致不等式.这适用于任何POSIX shell(dash,ksh,bash,zsh,busybox等).
注意`stty`提供了`-g`选项:`old_stty = $(stty -g); stty raw -echo; ...; stty"$ old_stty"`.这将完全恢复设置,这可能与`stty -sane`相同或不同.
`read answer`将在空格和换行符之前解释反斜杠,否则会剥离它们很少用于它们.根据[SC2162](https://github.com/koalaman/shellcheck/wiki/SC2162)使用`read -r answer`.

3> Pistos..:
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"


我不同意,因为它只实现了DOS中"是,否,取消"对话框的一部分功能.它无法实现的部分是输入检查...循环,直到收到有效的答案.
但原始问题描述未更改,并始终要求对是/否/取消提示进行响应.标题已经更新,比我原来的标题更清晰,但问题描述总是很清楚(在我看来).

4> yPhil..:

您可以使用内置的read命令; 使用该-p选项可向用户提示问题.

从BASH4开始,您现在可以使用-i建议答案,因此用户只需按下-e即可输入:

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

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

如果你想要一个"是/否"逻辑,你可以这样做:

read -e -p "
List the content of your home dir ? [Y/n] " YN

[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/


应该注意的是,`FILEPATH`是您选择的变量名,并通过命令提示符的答案进行设置.因此,如果您要运行`vlc"$ FILEPATH",例如,`vlc`将打开该文件.

5> Steven Huwig..:

Bash已经选择了这个目的.

select result in Yes No Cancel
do
    echo $result
done


+1 Geniously简单的解决方案.唯一的事情:这将提示并提示并提示......直到你在里面添加`exit` :)
但是,这不允许您输入y或n.您可以通过输入1 2或3进行选择.
(德皇:要打破它,只需输入EOT:`按Ctrl-D'但当然,使用它需要休息或身体的退出真正的代码.)
`exit`将一起退出脚本,`break`将只退出你所在的循环(如果你在`while`或`case`循环)

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



7> mpen..:

这是我放在一起的东西:

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

我是一个初学者,所以带上一粒盐,但似乎有效.


如果你将`case $ yn in`改为`case $ {yn: - $ 2} in`那么你可以使用第二个参数作为默认值Y或N.

8> 小智..:
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?"

...


如果案例开关是硬编码的,为什么我们提供'y'和'n'作为查询()的参数?那只是要求滥用.它们是固定参数,不是可变的,所以第2行的回声应该是:echo -n"$ 1 [Y/N]?"它们不能改变,因此不应该提供它们.
@MateuszPiotrowski自从我发表评论以来,答案已经过编辑和改进.您可以点击上面的"已编辑的12月23日"链接查看此答案的所有过去版本.早在2008年,代码就完全不同了.

9> olibre..:

你要:

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如果TTY,则调用命令

read -n 1 =>等一个字符

$'\e[1;32m ... \e[0m '=>以绿色打印
(绿色很好,因为白色/黑色背景都可读)

[[ $do_xxxx =~ ^(y|Y|)$ ]] => bash正则表达式

超时=>默认答案为否

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



10> Apurv Nerlek..:

使用最少行数实现此目的的最简单方法如下:

read -p " : y/n/cancel" CONDITION;

if [ "$CONDITION" == "y" ]; then
   # do something here!
fi

if只是一个例子:由你决定如何处理这个变量.



11> 小智..:

使用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

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



12> Dennis..:

此解决方案读取单个字符并在yes响应上调用函数.

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


@Jav echo在您的回复后会打印换行符。如果没有它,下一个要打印的内容将在您在同一行上回复后立即出现。尝试删除`echo`自己看看。

13> Jahid..:
read -e -p "Enter your choice: " choice

-e选项使用户可以使用箭头键编辑输入.

如果您想使用建议作为输入:

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

-i 选项打印暗示输入.



14> Walter A..:

您可以在REPLY上使用默认值read,将其转换为小写并与带有表达式的一组变量进行比较。
该脚本还支持ja/ si/oui

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



15> user9869932..:

很抱歉在这么老的帖子上发帖.几周前我遇到了类似的问题,在我的情况下,我需要一个解决方案,它也可以在一个在线安装程序脚本中工作,例如: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

希望这有助于某人.



16> Thorsten Sta..:

要获得一个很好的类似ncurses的输入框,请使用如下命令对话框:

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



17> Tom Hale..:

仅限单键按键

这是一个更长,但可重复使用和模块化的方法:

返回0=是和1=否

无需按下输入 - 只需一个字符

可以按下enter接受默认选择

可以禁用默认选项以强制选择

适用于zshbash.

按Enter键时默认为"no"

请注意,这N是资本.按Enter键,接受默认值:

$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]?

另请注意,这[y/N]?是自动附加的.接受默认的"no",因此不会回显任何内容.

重新提示,直到给出有效的回复:

$ 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- 要求yn

$ 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 ]] && 
# 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 
function confirm_yes {
  local prompt="${*:-Are you sure} [Y/n]? "
  get_yes_keypress "$prompt" 0
}



18> Yokai..:

我注意到没有人发布这样一个简单的用户输入显示多行回显菜单的答案,因此我可以这样做:

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

发布此方法是为了希望有人发现它有用且节省时间。

推荐阅读
周扒pi
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有