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


当前回答

在我的情况下,我需要从下载的脚本中读取。,

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

在这种情况下,read-ryn</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

其他回答

更通用的是:

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
}

您可以在读取时使用默认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

仅单个按键

这里有一个更长但可重复使用的模块化方法:

返回0=yes和1=no无需按enter键-仅需一个字符可以按enter键接受默认选择可以禁用默认选项以强制选择适用于zsh和bash。

按enter键时默认为“否”

请注意,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 *

按回车键时默认为“是”

请注意,Y是大写的:

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

上面,我刚刚按了回车键,所以命令运行了。

输入时无默认值-需要y或n

$ 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]?促使

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
}

这是我在脚本/函数中通常需要的:

如果按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!"

在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

显然,其他通信字符串在这里仍然没有翻译(Install,Answer),这需要在更完整的翻译中解决,但在许多情况下,即使是部分翻译也会很有用。

最后,请查看F.Hauri的优秀答案。