简体   繁体   English

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

[英]How do I prompt for Yes/No/Cancel input in a Linux shell script?

I want to pause input in a shell script, and prompt the user for choices.我想暂停 shell 脚本中的输入,并提示用户选择。
The standard Yes , No , or Cancel type question.标准YesNoCancel类型的问题。
How do I accomplish this in a typical bash prompt?如何在典型的 bash 提示符中完成此操作?

The simplest and most widely available method to get user input at a shell prompt is the read command.在 shell 提示符下获取用户输入的最简单和最广泛可用的方法是read命令。 The best way to illustrate its use is a simple demonstration:说明其使用的最好方法是一个简单的演示:

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

Another method, pointed out by Steven Huwig , is Bash's select command. Steven Huwig指出的另一种方法是 Bash 的select命令。 Here is the same example using 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

With select you don't need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice.使用select您不需要清理输入 - 它会显示可用的选项,然后您键入与您的选择相对应的数字。 It also loops automatically, so there's no need for a while true loop to retry if they give invalid input.它还会自动循环,因此如果它们提供无效输入,则无需使用while true循环重试。

Also, Léa Gris demonstrated a way to make the request language agnostic in her answer .此外, Léa Gris她的回答中演示了一种使请求语言不可知的方法。 Adapting my first example to better serve multiple languages might look like this:调整我的第一个示例以更好地服务于多种语言可能如下所示:

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

while true; do
    read -p "Install (${yesword} / ${noword})? " yn
    if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi
    if [[ "$yn" =~ $noexpr ]]; then exit; fi
    echo "Answer ${yesword} / ${noword}."
done

Obviously other communication strings remain untranslated here (Install, Answer) which would need to be addressed in a more fully completed translation, but even a partial translation would be helpful in many cases.显然,其他通信字符串在此处仍未翻译(安装、回答),这需要在更完整的翻译中解决,但在许多情况下,即使是部分翻译也会有所帮助。

Finally, please check out the excellent answer by F. Hauri .最后,请查看F. Hauri出色回答

At least five answers for one generic question.一个通用问题至少有五个答案。

Depending on根据

  • compliant: could work on poor systems with generic environments 兼容:可以在具有通用环境的较差系统上工作
  • specific: using so called bashisms特定于 :使用所谓的bashisms

and if you want如果你想

  • simple ``in line'' question / answer (generic solutions)简单的“在线”问题/答案(通用解决方案)
  • pretty formatted interfaces, like or more graphical using libgtk or libqt...漂亮的格式化界面,如或更多使用 libgtk 或 libqt 的图形...
  • use powerful readline history capability使用强大的 readline 历史记录功能

1. POSIX generic solutions 1. POSIX 通用解决方案

You could use the read command, followed by if ... then ... else :您可以使用read命令,后跟if ... then ... else

printf 'Is this a good question (y/n)? '
read answer
 # if echo "$answer" | grep -iq "^y" ;then
if [ "$answer" != "${answer#[Yy]}" ] ;then # this grammar (the #[] operator) means that the variable $answer where any Y or y in 1st position will be dropped if they exist.
    echo Yes
else
    echo No
fi

(Thanks to Adam Katz's comment : Replaced the test above with one that is more portable and avoids one fork:) (感谢Adam Katz 的评论:将上面的测试替换为更便携且避免使用分叉的测试:)

POSIX, but single key feature POSIX,但单键功能

But if you don't want the user to have to hit Return , you could write:但是,如果您不希望用户必须点击Return ,您可以编写:

( Edited: As @JonathanLeffler rightly suggest, saving stty's configuration could be better than simply force them to sane .) (已编辑:正如@JonathanLeffler正确建议的那样,保存stty 的配置可能比简单地强迫它们理智更好。)

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

Note: This was tested under , , , and !注意:这是在下测试的!

Same, but waiting explicitly for y or n :相同,但明确等待yn

#/bin/sh
printf '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

Using dedicated tools使用专用工具

There are many tools which were built using libncurses , libgtk , libqt or other graphical libraries.有许多使用libncurseslibgtklibqt或其他图形库构建的工具。 For example, using whiptail :例如,使用whiptail

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

Depending on your system, you may need to replace whiptail with another similiar tool:根据您的系统,您可能需要用另一个类似的工具替换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

where 20 is height of dialog box in number of lines and 60 is width of the dialog box.其中20是对话框的行数高度, 60是对话框的宽度。 These tools all have near same syntax.这些工具都具有几乎相同的语法。

DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...

2. Bash specific solutions 2. Bash具体解决方案

Basic in line method基本在线方法

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

I prefer to use case so I could even test for yes | ja | si | oui我更喜欢用case ,所以我什至可以测试yes | ja | si | oui yes | ja | si | oui yes | ja | si | oui if needed... yes | ja | si | oui如果需要...

in line with single key feature符合单键功能

Under bash, we can specify the length of intended input for for the read command:在 bash 下,我们可以为read命令指定预期输入的长度:

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

Under bash, read command accepts a timeout parameter, which could be useful.在 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. Some tricks for dedicated tools 3.专用工具的一些技巧

More sophisticated dialog boxes, beyond simple yes - no purposes:更复杂的对话框,除了简单的yes - no目的:

dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe

Progress bar:进度条:

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
) 

Little demo:小演示:

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

More samples?更多样品? Have a look at Using whiptail for choosing USB device and USB removable storage selector: USBKeyChooser看看使用whiptail选择USB设备USB可移动存储选择器:USBKeyChooser

5. Using readline's history 5.使用readline的历史

Example:例子:

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

This will create a file .myscript.history in your $HOME directory, than you could use readline's history commands, like Up , Down , Ctrl + r and others.这将在您的$HOME目录中创建一个文件.myscript.history ,而不是您可以使用 readline 的历史命令,如UpDownCtrl + r等。

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

You can use the built-in read command ;您可以使用内置的读取命令; Use the -p option to prompt the user with a question.使用-p选项向用户提示问题。

Since BASH4, you can now use -i to suggest an answer :从 BASH4 开始,您现在可以使用-i来建议答案:

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

(But remember to use the "readline" option -e to allow line editing with arrow keys) (但请记住使用“readline”选项-e以允许使用箭头键进行行编辑)

If you want a "yes / no" logic, you can do something like this:如果你想要一个“是/否”的逻辑,你可以这样做:

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

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

Bash has select for this purpose. Bash 为此选择了。

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

...

Here's something I put together:这是我整理的一些东西:

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

I'm a beginner, so take this with a grain of salt, but it seems to work.我是一个初学者,所以带着一粒盐,但它似乎工作。

You want:你要:

  • Bash builtin commands (ie portable) Bash 内置命令(即便携)
  • Check TTY检查 TTY
  • Default answer默认答案
  • Timeout暂停
  • Colored question有色问题

Snippet片段

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

Explanations解释

  • [[ -t 0 ]] && read ... => Call command read if TTY [[ -t 0 ]] && read ... => 调用命令read if TTY
  • read -n 1 => Wait for one character read -n 1 => 等待一个字符
  • $'\e[1;32m ... \e[0m ' => Print in green $'\e[1;32m ... \e[0m ' => 以绿色打印
    (green is fine because readable on both white/black backgrounds) (绿色很好,因为在白色/黑色背景上都可读)
  • [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash regex [[ $do_xxxx =~ ^(y|Y|)$ ]] => bash 正则表达式

Timeout => Default answer is No超时 => 默认答案为否

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

The easiest way to achieve this with the least number of lines is as follows:以最少的行数实现此目的的最简单方法如下:

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

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

The if is just an example: it is up to you how to handle this variable. if只是一个例子:如何处理这个变量取决于你。

Use the read command:使用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

and then all of the other stuff you need然后是你需要的所有其他东西

This solution reads a single character and calls a function on a yes response.此解决方案读取单个字符并在是响应时调用函数。

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

It is possible to handle a locale-aware "Yes / No choice" in a POSIX shell;可以在 POSIX shell 中处理区域设置感知的“是/否选择”; by using the entries of the LC_MESSAGES locale category, witch provides ready-made RegEx patterns to match an input, and strings for localized Yes No.通过使用LC_MESSAGES语言环境类别的条目,witch 提供现成的 RegEx 模式来匹配输入,以及本地化 Yes No 的字符串。

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

EDIT: As @Urhixidur mentioned in his comment :编辑:正如@Urhixidur他的评论中提到的:

Unfortunately, POSIX only specifies the first two (yesexpr and noexpr).不幸的是,POSIX 只指定了前两个(yesexpr 和 noexpr)。 On Ubuntu 16, yesstr and nostr are empty.在 Ubuntu 16 上,yesstr 和 nostr 为空。

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

LC_MESSAGES LC_MESSAGES

The yesstr and nostr locale keywords and the YESSTR and NOSTR langinfo items were formerly used to match user affirmative and negative responses. yesstrnostr语言环境关键字以及YESSTRNOSTR langinfo 项以前用于匹配用户的肯定和否定响应。 In POSIX.1-2008, the yesexpr , noexpr , YESEXPR , and NOEXPR extended regular expressions have replaced them.在 POSIX.1-2008 中, yesexprnoexprYESEXPRNOEXPR扩展正则表达式已取代它们。 Applications should use the general locale-based messaging facilities to issue prompting messages which include sample desired responses.应用程序应该使用通用的基于语言环境的消息传递工具来发出提示消息,其中包括示例所需的响应。

Alternatively using locales the Bash way:或者使用 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

The answer is matched using locale environment's provided regexps.答案是使用语言环境提供的正则表达式匹配的。

To translate the remaining messages, use bash --dump-po-strings scriptname to output the po strings for localization:要翻译剩余的消息,请使用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 ""

To get a nice ncurses-like inputbox use the command dialog like this:要获得类似 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

The dialog package is installed by default at least with SUSE Linux.对话框包至少在 SUSE Linux 中默认安装。 Looks like:好像: “对话”命令在行动

In my case I needed to read from a downloaded script ie,就我而言,我需要从下载的脚本中读取,即,

curl -Ss https://example.com/installer.sh | sh

The line read -r yn </dev/tty allowed it to read input in this case.在这种情况下, read -r yn </dev/tty行允许它读取输入。

printf "These files will be uploaded. Is this ok? (y/N) "
read -r yn </dev/tty

if [ "$yn" = "y" ]; then
   
   # Yes
else
   
   # No
fi

Single keypress only仅单次按键

Here's a longer, but reusable and modular approach:这是一种更长但可重用且模块化的方法:

  • Returns 0 =yes and 1 =no返回0 = 是和1 = 否
  • No pressing enter required - just a single character无需按 Enter - 只需一个字符
  • Can press enter to accept the default choice可以按回车接受默认选择
  • Can disable default choice to force a selection可以禁用默认选择以强制选择
  • Works for both zsh and bash .适用于zshbash

Defaulting to "no" when pressing enter按回车时默认为“否”

Note that the N is capitalsed.请注意, N是大写的。 Here enter is pressed, accepting the default:这里输入被按下,接受默认值:

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

Also note, that [y/N]?另请注意, [y/N]? was automatically appended.被自动附加。 The default "no" is accepted, so nothing is echoed.接受默认的“否”,因此不会回显任何内容。

Re-prompt until a valid response is given:重新提示直到给出有效响应:

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

Defaulting to "yes" when pressing enter按回车时默认为“是”

Note that the Y is capitalised:注意Y是大写的:

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

Above, I just pressed enter, so the command ran.上面,我只是按了回车,所以命令运行了。

No default on enter - require y or n输入时没有默认值 - 需要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

Here, 1 or false was returned.在这里,返回1或 false。 Note that with this lower-level function you'll need to provide your own [y/n]?请注意,使用此较低级别的功能,您需要提供自己的[y/n]? prompt.迅速的。

Code代码

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

You can use the default REPLY on a read , convert to lowercase and compare to a set of variables with an expression.您可以在read上使用默认REPLY ,转换为小写并使用表达式与一组变量进行比较。
The script also supports ja / si / oui该脚本还支持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
read -e -p "Enter your choice: " choice

The -e option enables the user to edit the input using arrow keys. -e选项使用户能够使用箭头键编辑输入。

If you want to use a suggestion as input:如果您想使用建议作为输入:

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

-i option prints a suggestive input. -i选项打印提示性输入。

I noticed that no one posted an answer showing multi-line echo menu for such simple user input so here is my go at it:我注意到没有人针对如此简单的用户输入发布显示多行回显菜单的答案,所以这是我的尝试:

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

This method was posted in the hopes that someone may find it useful and time-saving.发布此方法是希望有人会发现它有用且节省时间。

One-liner:单线:

read -p "Continue? [Enter] → Yes, [Ctrl]+[C] → No."

This assumes that "No" and "Cancel" have the same outcome, so no reason to treat them differently.这假设“否”和“取消”具有相同的结果,因此没有理由区别对待它们。

检查这个

read -p "Continue? (y/n): " confirm && [[ $confirm == [yY] || $confirm == [yY][eE][sS] ]] || exit 1

Multiple choice version:多选版本:

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
}

Example:例子:

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

It will set REPLY to y (inside the script).它将REPLY设置为y (在脚本内)。

Inspired by the answers of @Mark and @Myrddin I created this function for a universal prompt受@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
}

use it like this:像这样使用它:

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

I suggest you use dialog ...我建议你使用对话框...

Linux Apprentice: Improve Bash Shell Scripts Using Dialog Linux 学徒:使用对话框改进 Bash Shell 脚本

The dialog command enables the use of window boxes in shell scripts to make their use more interactive. dialog 命令允许在 shell 脚本中使用窗口框,以使它们的使用更具交互性。

it's simple and easy to use, there's also a gnome version called gdialog that takes the exact same parameters, but shows it GUI style on X.它简单易用,还有一个名为 gdialog 的 gnome 版本,它采用完全相同的参数,但在 X 上显示了 GUI 样式。

more generic would be:更通用的是:

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

One simple way to do this is with xargs -p or gnu parallel --interactive .一种简单的方法是使用xargs -p或 gnu parallel --interactive

I like the behavior of xargs a little better for this because it executes each command immediately after the prompt like other interactive unix commands, rather than collecting the yesses to run at the end.我更喜欢 xargs 的行为,因为它像其他交互式 unix 命令一样在提示符后立即执行每个命令,而不是收集 yesses 以在最后运行。 (You can Ctrl-C after you get through the ones you wanted.) (你可以通过你想要的那些后按Ctrl-C。)

eg,例如,

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

As a friend of a one line command I used the following:作为单行命令的朋友,我使用了以下命令:

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

Written longform, it works like this:书面长格式,它的工作原理是这样的:

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

I've used the case statement a couple of times in such a scenario, using the case statment is a good way to go about it.在这种情况下,我已经多次使用case语句,使用 case 语句是一个很好的方法。 A while loop, that ecapsulates the case block, that utilizes a boolean condition can be implemented in order to hold even more control of the program, and fulfill many other requirements.可以实现使用布尔条件封装case块的while循环,以便对程序进行更多控制,并满足许多其他要求。 After the all the conditions have been met, a break can be used which will pass control back to the main part of the program.在满足所有条件后,可以使用break将控制权交还给程序的主要部分。 Also, to meet other conditions, of course conditional statements can be added to accompany the control structures: case statement and possible while loop.此外,为了满足其他条件,当然可以添加条件语句来伴随控制结构: case语句和可能的while循环。

Example of using a case statement to fulfill your request使用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

Yes / No / Cancel是/否/取消

Function功能

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

Usage用法

case $(@confirm 'Confirm?') in
  Y ) echo "Yes" ;;
  N ) echo "No" ;;
  C ) echo "Cancel" ;;
esac

Confirm with clean user input使用干净的用户输入进行确认

Function功能

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

Usage用法

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

This is what I usually need in a script/function:这是我通常在脚本/函数中需要的:

  • default answer is Yes, if you hit ENTER默认答案是 Yes,如果你按 ENTER
  • accept also z (in case you mix up you are on QWERTZ Layout)也接受 z (如果你混淆了你在 QWERTZ 布局上)
  • accept other lanyuages ("ja", "Oui", ...)接受其他语言(“ja”,“Oui”,...)
  • handle the right exit in case you are inside a function如果您在函数内,请处理正确的出口
while true; do
    read -p "Continue [Y/n]? " -n 1 -r -e yn
    case "${yn:-Y}" in
        [YyZzOoJj]* ) echo; break ;;
        [Nn]* ) [[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 ;; # handle exits from shell or function but don't exit interactive shell
        * ) echo "Please answer yes or no.";;
    esac
done
echo "and off we go!"

The absolute most simple solution is this one-liner without clever tricks:绝对最简单的解决方案是这个没有巧妙技巧的单线:

read -p "press enter ..." y

It reminds of the classic DOS Hit any key to continue , except that it waits for the Enter key, not just any key.它让人想起经典的 DOS Hit any key to continue ,除了它等待 Enter 键,而不仅仅是任何键。

True, this does not offer you three options for Yes No Cancel, but it is useful where you accept control-C as No resp.诚然,这并没有为您提供 Yes No Cancel 的三个选项,但在您接受 control-C 作为 No resp 时它很有用。 Cancel in simple scripts like, eg:在简单的脚本中取消,例如:

#!/bin/sh
echo Backup this project
read -p "press enter ..." y
rsync -tavz . /media/hard_to_remember_path/backup/projects/yourproject/

because you don't like to need to remember ugly commands and paths, but neither scripts that run too fast, without giving you a chance to stop before you decide it is not the script you intended to run.因为您不喜欢记住丑陋的命令和路径,但既不记住运行速度过快的脚本,也不给您机会在您确定它不是您打算运行的脚本之前停下来。

In response to others:回应别人:

You don't need to specify case in BASH4 just use the ',,' to make a var lowercase.您不需要在 BASH4 中指定大小写,只需使用 ',,' 将 var 设为小写即可。 Also I strongly dislike putting code inside of the read block, get the result and deal with it outside of the read block IMO.此外,我非常不喜欢将代码放在读取块内,获取结果并在读取块 IMO 之外处理它。 Also include a 'q' for quit IMO.还包括退出 IMO 的“q”。 Lastly why type 'yes' just use -n1 and have the press y.最后,为什么键入“是”只需使用 -n1 并按 y。

Example: user can press y/n and also q to just quit.示例:用户可以按 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

Lots of good answers to this question, but from what I can see none of them are my ideal, which would:这个问题有很多很好的答案,但据我所知,它们都不是我的理想,它会:

  1. Be simple, just a couple lines of shell简单点,几行shell
  2. Work with a single y/n keypress (no need to press enter)使用单个 y/n 按键(无需按 Enter)
  3. Default to yes if you just hit enter如果您只是按 Enter,则默认为 yes
  4. Work with an uppercase Y/N as well也可以使用大写的 Y/N

Here's my version which does has those properties:这是我的版本,它确实具有这些属性:

read -n1 -p "Continue? (Y/n) " confirm

if ! echo $confirm | grep '^[Yy]\?$'; then
  exit 1
fi

You can modify that conditional to only run on "yes" (just remove the ! in the if statement) or add an else if you want to run code on both branches.您可以将该条件修改为仅在“是”上运行(只需删除if语句中的! ),或者如果您想在两个分支上运行代码,则添加else

I've made this small script for yes/no questions: https://github.com/optimistiCli/getans我为是/否问题制作了这个小脚本: https ://github.com/optimistiCli/getans

Example:例子:

#!/bin/bash

if ! getans.sh 'Shall we proceed?' y ; then
    echo "User said “NO”"
    exit 1
fi

echo "User said “YES”"
# do something usefull
exit 0

Direct link: https://github.com/optimistiCli/getans/raw/main/getans.sh直接链接: https ://github.com/optimistiCli/getans/raw/main/getans.sh

A one-liner python alternative using PyInquirer使用PyInquirer的单行python替代方案

python3 -c 'import PyInquirer; print(PyInquirer.prompt([{"type":"confirm", "message":"Do you want to continue?", "name":"r"}]).get("r"))'

which supports yes/no/cancel (intr, CTRL+C).它支持是/否/取消(intr,CTRL+C)。

在此处输入图像描述

You can write a function to test:你可以写一个函数来测试:

confirm() {
  local ans IFS=;
  while read -rp "$1" -n1 ans;
  do printf '\n';
    case $ans in [Yy]) return 0;;
      [Nn]) return 1;;
    esac;
  done;
}; ## Usage: if confirm "Are you sure? "; then ...

if confirm "Does everything look ok...reboot now? [Y/n]"; then
  echo "rebooting..."
  sleep 5
  reboot
fi

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM