在Bash中,如何添加“Are you sure [Y/n]”;到任何命令或别名?

在这种特殊情况下,我想在Bash中添加一个确认

Are you sure? [Y/n]

用于Mercurial的hg push ssh://username@www.example.com//somepath/morepath,它实际上是一个别名。是否有一个可以添加到别名的标准命令来实现它?

原因是hg pushhg out听起来很相似,有时当我想要hgoutrepo时,我可能会意外地键入hgpushrepo(两者都是别名)。

更新:如果它可以是类似于内置命令的另一个命令,比如:confirm && hg push ssh://...,那就太好了……只是一个命令,可以要求yesno,如果yes继续其余的。

230267 次浏览
以下是您想要的大致片段。 让我找出如何转发参数。

read -p "Are you sure you want to continue? <y/N> " prompt
if [[ $prompt == "y" || $prompt == "Y" || $prompt == "yes" || $prompt == "Yes" ]]
then
# http://stackoverflow.com/questions/1537673/how-do-i-forward-parameters-to-other-command-in-bash-script
else
exit 0
fi

注意yes | command name here:)

为了避免显式检查'yes'的这些变体,你可以使用bash正则表达式操作符'=~'和一个正则表达式:

read -p "Are you sure you want to continue? <y/N> " prompt
if [[ $prompt =~ [yY](es)* ]]
then
(etc...)

它测试用户输入是否以“y”或“y”开头,后面是0个或多个“es”。

这些是< >强哈米什的< / >强劲的答案更紧凑和通用的形式。它们可以处理任何大小写字母的混合:

read -r -p "Are you sure? [y/N] " response
case "$response" in
[yY][eE][sS]|[yY])
do_something
;;
*)
do_something_else
;;
esac

或者,对于Bash >= version 3.2:

read -r -p "Are you sure? [y/N] " response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]
then
do_something
else
do_something_else
fi

注意:如果$response是一个空字符串,它将给出一个错误。要修复,只需添加引号:"$response"。-在包含字符串的变量中总是使用双引号(例如:更喜欢使用"$@"而不是$@)。

或者,Bash 4.x:

read -r -p "Are you sure? [y/N] " response
response=${response,,}    # tolower
if [[ "$response" =~ ^(yes|y)$ ]]
...

编辑:

作为对你的编辑的回应,下面是你如何根据我回答中的第一个版本创建和使用confirm命令(它将与其他两个类似地工作):

confirm() {
# call with a prompt string or use a default
read -r -p "${1:-Are you sure? [y/N]} " response
case "$response" in
[yY][eE][sS]|[yY])
true
;;
*)
false
;;
esac
}

使用此函数:

confirm && hg push ssh://..

confirm "Would you really like to do a push?" && hg push ssh://..

在/etc/bashrc文件中添加以下内容。 这个脚本添加了一个常驻的“function”,而不是一个别名“confirm”


function confirm( )
{
#alert the user what they are about to do.
echo "About to $@....";
#confirm with the user
read -r -p "Are you sure? [Y/n]" response
case "$response" in
[yY][eE][sS]|[yY])
#if yes, then execute the passed parameters
"$@"
;;
*)
#Otherwise exit...
echo "ciao..."
exit
;;
esac
}

这并不完全是一个“询问是或否”,而只是一个hack:别名hg push ...不是hgpushrepo,而是hgpushrepoconfirmedpush,当我可以拼写出整个事情的时候,左脑已经做出了一个合乎逻辑的选择。

read -r -p "Are you sure? [Y/n]" response
response=${response,,} # tolower
if [[ $response =~ ^(yes|y| ) ]] || [[ -z $response ]]; then
your-action-here
fi

这可能是一个黑客:

如问题在Unix / Bash中,是"xargs -p"在运行任何命令之前提示确认的好方法?

我们可以使用xargs来完成这项工作:

echo ssh://username@www.example.com//somepath/morepath | xargs -p hg push

当然,这将被设置为一个别名,如hgpushrepo

例子:

$ echo foo | xargs -p ls -l
ls -l foo?...y
-rw-r--r--  1 mikelee    staff  0 Nov 23 10:38 foo


$ echo foo | xargs -p ls -l
ls -l foo?...n


$

在游戏后期,但我创建了前面答案的confirm函数的另一个变体:

confirm ()
{
read -r -p "$(echo $@) ? [y/N] " YESNO


if [ "$YESNO" != "y" ]; then
echo >&2 "Aborting"
exit 1
fi


CMD="$1"
shift


while [ -n "$1" ]; do
echo -en "$1\0"
shift
done | xargs -0 "$CMD" || exit $?
}

使用它:

confirm your_command

特点:

  • 打印您的命令作为提示符的一部分
  • 使用NULL分隔符传递参数
  • 保留命令的退出状态

错误:

  • echo -en可以与bash一起工作,但在shell中可能会失败
  • 可能失败,如果参数干扰echoxargs
  • 因为编写shell脚本很难,所以有无数的其他bug

试,

 #!/bin/bash
pause ()
{
REPLY=Y
while [ "$REPLY" == "Y" ] || [ "$REPLY" != "y" ]
do
echo -e "\t\tPress 'y' to continue\t\t\tPress 'n' to quit"
read -n1 -s
case "$REPLY" in
"n")  exit                      ;;
"N")  echo "case sensitive!!"   ;;
"y")  clear                     ;;
"Y")  echo "case sensitive!!"   ;;
* )  echo "$REPLY is Invalid Option"     ;;
esac
done
}
pause
echo "Hi"

使用回车符可以很容易地绕过确认,并且我发现连续提示有效输入很有用。

这里有一个函数可以简化。如果没有接收到Y|N,则显示红色“无效输入”,并再次提示用户。

prompt_confirm() {
while true; do
read -r -n 1 -p "${1:-Continue?} [y/n]: " REPLY
case $REPLY in
[yY]) echo ; return 0 ;;
[nN]) echo ; return 1 ;;
*) printf " \033[31m %s \n\033[0m" "invalid input"
esac
done
}


# example usage
prompt_confirm "Overwrite File?" || exit 0

您可以通过传递参数来更改默认提示符

不一样,但不管怎样都行得通。

#!/bin/bash
i='y'
while [ ${i:0:1} != n ]
do
# Command(s)
read -p " Again? Y/n " i
[[ ${#i} -eq 0 ]] && i='y'
done
< br > < p >输出: 一遍吗?Y / n n < br > 一遍吗?Y / n任何< br > 一遍吗?Y / n 7 < br > 一遍吗?Y / n, < br > 一遍吗?Y / n nsijf < br > $ < / p >

现在只检查$i读取的第一个字符。

好吧,这是我的confirm版本,从James的修改:

function confirm() {
local response msg="${1:-Are you sure} (y/[n])? "; shift
read -r $* -p "$msg" response || echo
case "$response" in
[yY][eE][sS]|[yY]) return 0 ;;
*) return 1 ;;
esac
}

这些变化是:

  1. 使用local来防止变量名发生冲突
  2. read使用$2 $3 ...来控制它的动作,所以你可以使用-n-t
  3. 如果read退出失败,echo换行为美
  4. 我的Git on Windows只有bash-3.1,没有truefalse,所以用return代替。当然,这也兼容bash-4.4 (Git for Windows的当前版本)。
  5. 使用ipython风格的"(y/[n])"明确表示"n"是默认值。

下面的代码结合了两个东西

  1. shop -s nocasmatch将照顾大小写不敏感

  2. 和if条件,它将接受两个输入,你传递yes yes yes y。

    shop -s nocasematch

    如果[[sed-4.2.2.]$LINE =~ (yes|y)$]]

    然后退出0

    fi < / p >

这是我的解决方案,使用本地化正则表达式。所以在德语中,“Ja”的“j”也会被解释为“是”。

第一个参数是问题,如果第二个参数是y,那么yes将是默认答案,否则no将是默认答案。如果答案是“是”,则返回值为0,如果答案是“否”,则返回值为1。

function shure(){
if [ $# -gt 1 ] && [[ "$2" =~ ^[yY]*$ ]] ; then
arg="[Y/n]"
reg=$(locale noexpr)
default=(0 1)
else
arg="[y/N]"
reg=$(locale yesexpr)
default=(1 0)
fi
read -p "$1 ${arg}? : " answer
[[ "$answer" =~ $reg ]] && return ${default[1]} || return ${default[0]}
}

下面是一个基本用法

# basic example default is no
shure "question message" && echo "answer yes" || echo "answer no"
# print "question message [y/N]? : "


# basic example default set to yes
shure "question message" y && echo "answer yes" || echo "answer no"
# print "question message [Y/n]? : "

这可能有点太短了,但对于我自己的私人使用,它工作得很好

read -n 1 -p "Push master upstream? [Y/n] " reply;
if [ "$reply" != "" ]; then echo; fi
if [ "$reply" = "${reply#[Nn]}" ]; then
git push upstream master
fi

read -n 1只读取一个字符。不需要按回车键。如果不是“n”或“n”,则假定是“Y”。按回车键也意味着Y。

(至于真正的问题:使它成为一个bash脚本,并更改您的别名指向该脚本,而不是之前所指向的内容)

不需要按enter

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

  • 返回0=yes和1=no
  • 不需要按enter键-只需一个字符
  • 可以按输入接受默认选择吗
  • 可以禁用默认选择来强制选择吗
  • 适用于zshbash

默认为“不”;当按enter键时

注意N是大写的。此处按下enter键,接受默认值:

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

默认为“yes”;当按enter键时

注意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"
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
}

这个版本允许你有多个情况yYnN

  1. 可选:重复问题,直到提供批准问题

  2. 可选:忽略任何其他答案

  3. 可选:退出终端

    confirm() {
    echo -n "Continue? y or n? "
    read REPLY
    case $REPLY in
    [Yy]) echo 'yup y' ;; # you can change what you do here for instance
    [Nn]) break ;;        # exit case statement gracefully
    # Here are a few optional options to choose between
    # Any other answer:
    
    
    # 1. Repeat the question
    *) confirm ;;
    
    
    # 2. ignore
    # *) ;;
    
    
    # 3. Exit terminal
    # *) exit ;;
    
    
    esac
    # REPLY=''
    }
    

Notice this too: On the last line of this function clear the REPLY variable. Otherwise if you echo $REPLY you will see it is still set until you open or close your terminal or set it again.

我知道这是一个老问题,但这可能会帮助到一些人,它还没有在这里解决。

有人问我如何在接收文件输入的脚本中使用rm -i。由于脚本的文件输入通常是从STDIN接收的,因此我们需要更改它,以便从STDIN接收对rm命令的响应。下面是解决方案:

#!/bin/bash
while read -u 3 line
do
echo -n "Remove file $line?"
read -u 1 -n 1 key
[[ $key = "y" ]] &&  rm "$line"
echo
done 3<filelist

如果ANY键不是"y"键(仅限小写)按下,文件将不会被删除。没有必要在键后按回车键(因此echo命令发送新行到显示器)。 注意,POSIX bash "read"命令不支持-u开关,因此需要寻找解决方案