簡體   English   中英

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

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

我想暫停 shell 腳本中的輸入,並提示用戶選擇。
標准YesNoCancel類型的問題。
如何在典型的 bash 提示符中完成此操作?

在 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循環重試。

此外, Léa Gris她的回答中演示了一種使請求語言不可知的方法。 調整我的第一個示例以更好地服務於多種語言可能如下所示:

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

顯然,其他通信字符串在此處仍未翻譯(安裝、回答),這需要在更完整的翻譯中解決,但在許多情況下,即使是部分翻譯也會有所幫助。

最后,請查看F. Hauri出色回答

一個通用問題至少有五個答案。

根據

  • 兼容:可以在具有通用環境的較差系統上工作
  • 特定於 :使用所謂的bashisms

如果你想

  • 簡單的“在線”問題/答案(通用解決方案)
  • 漂亮的格式化界面,如或更多使用 libgtk 或 libqt 的圖形...
  • 使用強大的 readline 歷史記錄功能

1. POSIX 通用解決方案

您可以使用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

(感謝Adam Katz 的評論:將上面的測試替換為更便攜且避免使用分叉的測試:)

POSIX,但單鍵功能

但是,如果您不希望用戶必須點擊Return ,您可以編寫:

(已編輯:正如@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

注意:這是在下測試的!

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

使用專用工具

有許多使用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 yes | ja | si | oui 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

更多樣品? 看看使用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

這將在您的$HOME目錄中創建一個文件.myscript.history ,而不是您可以使用 readline 的歷史命令,如UpDownCtrl + r等。

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

您可以使用內置的讀取命令; 使用-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 ~/

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

...

這是我整理的一些東西:

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

以最少的行數實現此目的的最簡單方法如下:

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

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

if只是一個例子:如何處理這個變量取決於你。

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

然后是你需要的所有其他東西

此解決方案讀取單個字符並在是響應時調用函數。

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

可以在 POSIX shell 中處理區域設置感知的“是/否選擇”; 通過使用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

編輯:正如@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語言環境關鍵字以及YESSTRNOSTR langinfo 項以前用於匹配用戶的肯定和否定響應。 在 POSIX.1-2008 中, yesexprnoexprYESEXPRNOEXPR擴展正則表達式已取代它們。 應用程序應該使用通用的基於語言環境的消息傳遞工具來發出提示消息,其中包括示例所需的響應。

或者使用 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 ""

要獲得類似 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 中默認安裝。 好像: “對話”命令在行動

就我而言,我需要從下載的腳本中讀取,即,

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

在這種情況下, 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

僅單次按鍵

這是一種更長但可重用且模塊化的方法:

  • 返回0 = 是和1 = 否
  • 無需按 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 *

上面,我只是按了回車,所以命令運行了。

輸入時沒有默認值 - 需要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
}

您可以在read上使用默認REPLY ,轉換為小寫並使用表達式與一組變量進行比較。
該腳本還支持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

-e選項使用戶能夠使用箭頭鍵編輯輸入。

如果您想使用建議作為輸入:

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

-i選項打印提示性輸入。

我注意到沒有人針對如此簡單的用戶輸入發布顯示多行回顯菜單的答案,所以這是我的嘗試:

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

發布此方法是希望有人會發現它有用且節省時間。

單線:

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

這假設“否”和“取消”具有相同的結果,因此沒有理由區別對待它們。

檢查這個

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

多選版本:

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 答案的啟發,我為通用提示創建了這個函數

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"

我建議你使用對話框...

Linux 學徒:使用對話框改進 Bash Shell 腳本

dialog 命令允許在 shell 腳本中使用窗口框,以使它們的使用更具交互性。

它簡單易用,還有一個名為 gdialog 的 gnome 版本,它采用完全相同的參數,但在 X 上顯示了 GUI 樣式。

更通用的是:

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

一種簡單的方法是使用xargs -p或 gnu parallel --interactive

我更喜歡 xargs 的行為,因為它像其他交互式 unix 命令一樣在提示符后立即執行每個命令,而不是收集 yesses 以在最后運行。 (你可以通過你想要的那些后按Ctrl-C。)

例如,

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

作為單行命令的朋友,我使用了以下命令:

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

在這種情況下,我已經多次使用case語句,使用 case 語句是一個很好的方法。 可以實現使用布爾條件封裝case塊的while循環,以便對程序進行更多控制,並滿足許多其他要求。 在滿足所有條件后,可以使用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, 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

是/否/取消

功能

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

這是我通常在腳本/函數中需要的:

  • 默認答案是 Yes,如果你按 ENTER
  • 也接受 z (如果你混淆了你在 QWERTZ 布局上)
  • 接受其他語言(“ja”,“Oui”,...)
  • 如果您在函數內,請處理正確的出口
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!"

絕對最簡單的解決方案是這個沒有巧妙技巧的單線:

read -p "press enter ..." y

它讓人想起經典的 DOS Hit any key to continue ,除了它等待 Enter 鍵,而不僅僅是任何鍵。

誠然,這並沒有為您提供 Yes No Cancel 的三個選項,但在您接受 control-C 作為 No resp 時它很有用。 在簡單的腳本中取消,例如:

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

因為您不喜歡記住丑陋的命令和路徑,但既不記住運行速度過快的腳本,也不給您機會在您確定它不是您打算運行的腳本之前停下來。

回應別人:

您不需要在 BASH4 中指定大小寫,只需使用 ',,' 將 var 設為小寫即可。 此外,我非常不喜歡將代碼放在讀取塊內,獲取結果並在讀取塊 IMO 之外處理它。 還包括退出 IMO 的“q”。 最后,為什么鍵入“是”只需使用 -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

這個問題有很多很好的答案,但據我所知,它們都不是我的理想,它會:

  1. 簡單點,幾行shell
  2. 使用單個 y/n 按鍵(無需按 Enter)
  3. 如果您只是按 Enter,則默認為 yes
  4. 也可以使用大寫的 Y/N

這是我的版本,它確實具有這些屬性:

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

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

您可以將該條件修改為僅在“是”上運行(只需刪除if語句中的! ),或者如果您想在兩個分支上運行代碼,則添加else

我為是/否問題制作了這個小腳本: https ://github.com/optimistiCli/getans

例子:

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

直接鏈接: https ://github.com/optimistiCli/getans/raw/main/getans.sh

使用PyInquirer的單行python替代方案

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

它支持是/否/取消(intr,CTRL+C)。

在此處輸入圖像描述

你可以寫一個函數來測試:

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