如何在Linux Shell脚本中提示输入Yes / No / Cancel?


1443

我想在shell脚本中暂停输入,并提示用户进行选择。
标准YesNoCancel类型问题。
如何在典型的bash提示中完成此操作?


11
就像一个注释:提示约定是这样的:如果您提供一个[yn]选项,则大写的选项是默认的,即[Yn]默认为“是”,[yN]默认为“否”。参见ux.stackexchange.com/a/40445/43532
Tyzoid

任何来自ZSH的人,请参见此答案以了解如何使用read命令进行提示
smac89

Answers:


1615

该命令是在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的命令。这是使用的相同示例:selectselect

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如果输入无效,则无需重试循环。

此外,LEA格里斯表现出一种方法,使在请求语言无关她的回答。修改我的第一个示例以更好地服务于多种语言可能看起来像这样:

set -- $(locale LC_MESSAGES)
yesptrn="$1"; noptrn="$2"; yesword="$3"; noword="$4"

while true; do
    read -p "Install (${yesword} / ${noword})? " yn
    case $yn in
        ${yesptrn##^} ) make install; break;;
        ${noptrn##^} ) exit;;
        * ) echo "Answer ${yesword} / ${noword}.";;
    esac
done

显然,这里没有翻译其他通信字符串(安装,回答),这需要通过更完整的翻译来解决,但是在许多情况下,即使是部分翻译也将有所帮助。

最后,请检查出的出色答卷F. Hauri


30
在OS X Leopard中使用Bash时,我更改exitbreak在选择“否”时不关闭选项卡。
Trey Piepmeier 09年

1
大于“是”或“否”的选项如何使用?在case子句中,您是否编写类似的内容:安装程序,之后不执行任何操作)打破;
肖恩

1
@Shawn使用read当然可以使用bash shell的switch语句支持的任何glob或regex模式。它只会匹配键入到您的模式的文本,直到找到匹配的文本。使用select,将选择列表提供给命令,并将其显示给用户。您可以根据需要使列表中的项目长或短。我建议检查他们的手册页以获得全面的使用信息。
Myrddin Emrys,2012年

3
为什么出现breakselect如果没有循环?
2014年

1
@akostadinov我真的应该多阅读我的编辑历史记录。您是正确的,我错了,我是根据Jayan的建议引入了一个错误,哈哈。该错误后来得到修复,但编辑距离相差甚远,我什至都不记得对其进行更改。
Myrddin Emrys 2014年

524

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

取决于

  • 兼容:可以在通用的较差系统上工作 环境
  • 具体:使用所谓的bashisms

如果你想

  • 简单的``在线''问题/答案(通用解决方案)
  • 漂亮的格式化接口,例如 或更多使用libgtk或libqt的图形...
  • 使用强大的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

注意:这是在

相同,但显式等待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或其他图形库。例如,使用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

3. 专用工具的一些技巧

除简单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

更多样品?看看使用鞭子选择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目录中,比你能使用输入行的历史命令,比如UpDownCtrl+ r等。


4
请注意,stty提供了-g使用选项:old_stty=$(stty -g); stty raw -echo; …; stty "$old_stty"。这将完全恢复找到的设置,该设置可能与可能相同stty -sane
Jonathan Leffler

3
read answer会在空格和换行符之前解释反斜杠,否则会删除它们,这是很少使用的。read -r answer根据SC2162使用代替。
vlfig

1
“使用readline的历史记录”方法对于OP的问题非常不合适。我很高兴您无论如何都包含了它。我有数十种要使用该模式进行更新的更复杂的脚本!
布鲁诺·布鲁诺斯基

5
您可以将casePOSIX和bash一起使用(使用通配符条件而不是bash子字符串:)case $answer in; [Yy]* ) echo Yes ;;,但是我更喜欢使用条件语句,而不是[ "$answer" != "${answer#[Yy]}" ]您的echo "$answer" | grep -iq ^y。它更可移植(某些非GNU greps无法-q正确实现),并且没有系统调用。 ${answer#[Yy]}使用参数扩展来删除Yy从的开头开始($answer如果存在),则导致不平等。这适用于任何POSIX Shell(破折号,ksh,bash,zsh,busybox等)。
亚当·卡兹

1
@CarterPape是的,这是个玩笑!但是,在这个详尽的答案中,您可能会发现很多提示(第二点提出了至少3种不同的方法)!而且...从大约5年开始,您首先要讲述我的计数方法!;
F. Hauri

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

25
我不同意,因为它仅实现了DOS中“是,否,取消”对话框的部分功能。它无法实现的部分是输入检查...循环,直到收到有效答案为止。
Myrddin Emrys

1
(原始问题标题为“如何提示在Linux Shell脚本中输入?”)
Pistos

8
但是原始问题描述没有改变,并且总是要求对是/否/取消提示做出回应。标题已更新,比我原来的标题更清晰,但问题描述始终很清楚(我认为)。
Myrddin Emrys

165

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

从BASH4开始,您现在可以使用-i以下建议:

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

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

-e在第二个示例中(简单的是/否)有什么好处?
JBallin

-e -p-ep什么理由要使用?
JBallin

1
没有-e标志/选项,您可能(取决于实现)可能无法键入“ y”,然后改变主意并用“ n”(或与此相关的其他任何内容)替换;在记录命令时,除其他原因外,出于可读性/清晰度的考虑,单独列出选项更好。
yPhil

109

Bash 为此选择了。

select result in Yes No Cancel
do
    echo $result
done

18
+1非常简单的解决方案。唯一的事情:这将提示并提示并提示...,直到您添加一个exit内部:)
kaiser

5
(kaiser:要中断它,只需输入EOT :Ctrl-D。但是,当然,使用它的实际代码将需要在体内断裂或退出。)
Zorawar 2014年

12
这会不会让你输入y或n,though.You选择通过输入1 2或3
djjeck

3
exit将一起退出脚本,break仅退出您所在的循环(如果您位于“ while或” case循环上)
wranvaud

57
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

36

这是我整理的:

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

我是一个初学者,所以可以加一点盐,但是似乎有效。


9
如果更改case $yn in为,case ${yn:-$2} in则可以将第二个参数用作默认值Y或
N。– jchook

1
或更改case $yncase "${yn:-Y}"有是默认
rubo77

35
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?"

...

1
在每行的开头放置四个空格,以保留代码的格式。
Jouni K.Seppänen08年

10
如果大小写开关是硬编码的,为什么还要提供'y'和'n'作为参数来查询()?那只是在要求滥用。它们是固定参数,不可更改,因此第2行上的echo应当显示为:echo -n“ $ 1 [Y / N]?”无法更改,因此不应提供它们。
Myrddin Emrys

1
@MyrddinEmrys请您详细说明一下?或发布指向文章或几个关键字的链接,以便我自己进行研究。
Mateusz Piotrowski

4
@MateuszPiotrowski自从我发表评论以来,答案已得到编辑和改进。您可以点击上方的“经过编辑的12月23日”链接,以查看此答案的所有过去版本。早在2008年,代码就大为不同。
Myrddin Emrys

27

你要:

  • 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

26

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

read -p "<Your Friendly Message here> : y/n/cancel" CONDITION;

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

if仅仅是一个例子:它是由你如何处理这个变量。


20

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

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


17

此解决方案读取单个字符并在“是”响应上调用一个函数。

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

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

13

要获得类似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一起安装了该对话框包。好像: 运行中的“对话框”命令


12
read -e -p "Enter your choice: " choice

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

如果要使用建议作为输入:

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

-i 选项打印提示性输入。


邑,-e -i不要在SH(Bourne shell)中工作,但问题是标签bash的具体..
Jahid

12

您可以在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

12

可以在POSIX Shell中处理区域设置感知的“是/否选择”。通过使用LC_MESSAGES语言环境类别的条目,witch提供了现成的RegEx模式以匹配输入,并提供了用于本地化的字符串。是否。

#!/usr/bin/env sh

# Getting LC_MESSAGES values into variables
# shellcheck disable=SC2046 # Intended IFS splitting
IFS='
' set -- $(locale LC_MESSAGES)

yesexpr="$1"
noexpr="$2"
yesstr="$3"
nostr="$4"
messages_codeset="$5" # unused here, but kept as documentation

# Display Yes / No ? prompt into locale
echo "$yesstr / $nostr ?"

# Read answer
read -r yn

# Test answer
case "$yn" in
# match only work with the character class from the expression
  ${yesexpr##^}) echo "answer $yesstr" ;;
  ${noexpr##^}) echo "answer $nostr" ;;
esac

编辑:正如@Urhixidur他的评论中提到的:

不幸的是,POSIX只指定了前两个(yesexpr和noexpr)。在Ubuntu 16上,yesstr和nostr为空。

参见:https : //www.ee.ryerson.ca/~courses/ele709/susv4/xrat/V4_xbd_chap07.html#tag_21_07_03_06

LC_MESSAGES

yesstrnostr现场的关键字和YESSTRNOSTRlanginfo项目以前用于匹配用户的肯定和否定响应。在POSIX.1-2008的yesexprnoexprYESEXPR,和NOEXPR扩展正则表达式的替代者。应用程序应使用基于常规语言环境的常规消息传递功能来发出提示消息,其中包括所需的示例响应。

或者使用Bash方式使用语言环境:

#!/usr/bin/env bash

IFS=$'\n' read -r -d '' yesexpr noexpr _ < <(locale LC_MESSAGES)

printf -v yes_or_no_regex "(%s)|(%s)" "$yesexpr" "$noexpr"

printf -v prompt $"Please answer Yes (%s) or No (%s): " "$yesexpr" "$noexpr"

declare -- answer=;

until [[ "$answer" =~ $yes_or_no_regex ]]; do
  read -rp "$prompt" answer
done

if [[ -n "${BASH_REMATCH[1]}" ]]; then
  echo $"You answered: Yes"
else
  echo $"No, was your answer."
fi

使用语言环境环境提供的正则表达式来匹配答案。

要翻译其余消息,请使用bash --dump-po-strings scriptname输出po字符串进行本地化:

#: scriptname:8
msgid "Please answer Yes (%s) or No (%s): "
msgstr ""
#: scriptname:17
msgid "You answered: Yes"
msgstr ""
#: scriptname:19
msgid "No, was your answer."
msgstr ""

我喜欢添加语言不可知选项。做得好。
Myrddin Emrys

1
不幸的是,POSIX仅指定了前两个(yesexpr和noexpr)。在Ubuntu 16上,yesstr和nostr为空。
Urhixidur

1
可是等等!有更坏的消息!bash case语句表达式不是正则表达式,它们是文件名表达式。因此,Ubuntu 16的yesexpr和noexpr(分别为“ ^ [yY]。*”和“ ^ [nN]。*”)将由于嵌入期间而彻底失败。在正则表达式中,“。*”表示“零次或多次非换行符”。但是在一个case语句中,它是一个文字“。”。后跟任意数量的字符。
Urhixidur

1
最后,该是可以做到的最好yesexpr,并noexpr在shell环境,是用它击的特定正则表达式匹配if [[ "$yn" =~ $yesexpr ]]; then echo $"Answered yes"; else echo $"Answered no"; fi
LEA皮诺

10

仅单键

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

  • 返回0= y和1= no
  • 无需按下Enter键-只需一个字符
  • 可以按enter接受默认选择
  • 可以禁用默认选择以强制选择
  • 适用于zshbash

按下回车键时默认为“否”

请注意,N大写。在这里按下回车键,接受默认值:

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

按下回车键时默认为“是”

请注意,将Y大写:

$ confirm_yes "Show dangerous command" && echo "rm *"
Show dangerous command [Y/n]?
rm *

在上面,我只是按下Enter键,所以命令运行了。

无默认开启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 ]] && </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 '\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
}

上面我当我测试了这个剧本,而不是outut,Show dangerous command [y/N]? [y/n]?Show dangerous command [Y/n]? [y/n]?
伊利亚斯·卡里姆

谢谢@IliasKarim,我刚刚解决了这个问题。
汤姆·黑尔'18

9

很抱歉在这样的旧帖子上发帖。几周前,我遇到了类似的问题,就我而言,我需要一个可以在在线安装脚本中使用的解决方案,例如: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

希望这对某人有帮助。


其中一个重要部分是输入验证。我认为,像第一个例子tty那样适应我的第一个示例来接受输入,对您来说也会做得很好,并且还会循环输入错误的输入(想象一下缓冲区中的几个字符;您的方法将迫使用户始终选择否)。
Myrddin Emrys

7

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

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

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


4

多选版本:

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
$

它将设置REPLYy(在脚本内部)。


4

我建议你使用对话框 ...

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

对话框命令使您可以在外壳程序脚本中使用窗口框,以使它们的使用更具交互性。

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


对于不经意的读者,请使用此处的对话框命令查看代码段:stackoverflow.com/a/22893526/363573
Stephan

4

受到@Mark和@Myrddin答案的启发,我为通用提示创建了此功能

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 : " "a x f" selection
echo "$selection"

4

更通用的是:

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
}

3

一种简单的方法是with xargs -p或gnu parallel --interactive

为此,我更喜欢xargs的行为,因为它像其他交互式unix命令一样,在提示符后立即执行每个命令,而不是收集yeses来最后运行。(完成所需的操作后,可以按Ctrl-C。)

例如,

echo *.xml | xargs -p -n 1 -J {} mv {} backup/

不错,但xargs --interactive仅限于是或否。只要满足您的需求就足够了,但是我最初的问题举了一个例子,给出了三种可能的结果。我真的很喜欢它,尽管它很流。许多常见的情况都将受益于它的管道传输能力。
Myrddin Emrys 2015年

我知道了。我的想法是,“取消”意味着仅停止所有进一步的执行(这通过Ctrl-C支持),但是如果您需要在取消(或不执行)时执行更复杂的操作,这将无法满足要求。
约书亚·戈德堡

3

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

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

书面的长格式,它是这样的:

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

您能否阐明提示变量的用法?在我看来,它就像被一根衬板擦掉了,那么您如何使用生产线来做任何事情?
Myrddin Emrys

while循环后会清除提示。因为我希望在以后初始化提示变量(因为我使用语句的频率更高)。仅在键入y | Y时在shell脚本中包含此行,如果键入n | N或重复要求输入其他所有内容,则退出。
ccDict

3

case在这种情况下,我已经使用过几次该语句,使用case语句是解决该问题的好方法。甲while环,即ecapsulates的case块,利用一个布尔条件可以为了保持程序的甚至更多的控制,并且满足许多其它要求来实现。满足所有条件后,break可以使用a 将控制权交还给程序的主要部分。同样,为了满足其他条件,当然可以添加条件语句以伴随控制结构: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

3

是/否/取消

功能

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

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

这似乎复杂而脆弱。怎么样yn(){ read -s -n 1 -p '[y/n]'; test "$REPLY" = "y" ; } yn && echo success || echo failure
三胞胎

2

回应他人:

您无需在BASH4中指定大小写,只需使用','即可使var小写。我也非常不喜欢将代码放入读取块中,获取结果并在读取块IMO之外进行处理。还包括退出海事组织的“ q”。最后,为什么键入'yes'只是使用-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

0

在大多数情况下,您需要继续执行脚本,直到用户继续输入“是”为止,并且仅在用户输入“否”时才停止。以下代码段将帮助您实现这一目标!

#!/bin/bash
input="yes"
while [ "$input" == "yes" ]
do
  echo "execute script functionality here..!!"
  echo "Do you want to continue (yes/no)?"
  read input
done
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.