在bash中,如何向任何命令或别名添加”您确定[Y/N]吗?”

In Bash, how to add “Are you sure [Y/n]” to any command or alias?

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

1
Are you sure? [Y/n]

对于Mercurial的hg push ssh://[email protected]//somepath/morepath,它实际上是一个别名。是否有一个标准命令可以添加到别名中以实现它?

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

更新:如果它可以像一个内置命令和另一个命令,例如:confirm && hg push ssh://...,那就太好了…只是一个命令,它可以请求一个yesno,如果yes,则继续执行其余的命令。


这些都是哈米什答案的更紧凑和更广泛的形式。它们处理任何大小写字母的混合:

1
2
3
4
5
6
7
8
9
read -r -p"Are you sure? [y/N]" response
case"$response" in
    [yY][eE][sS]|[yY])
        do_something
        ;;
    *)
        do_something_else
        ;;
esac

或者,对于bash>=3.2版:

1
2
3
4
5
6
7
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。

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

编辑:

为了响应您的编辑,下面介绍如何根据我的答案中的第一个版本创建和使用confirm命令(它与其他两个命令的工作方式类似):

1
2
3
4
5
6
7
8
9
10
11
12
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
}

要使用此功能:

1
confirm && hg push ssh://..

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


这大概是你想要的一个片段。我来看看如何提出论点。

1
2
3
4
5
6
7
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正则表达式运算符"=~"与正则表达式一起使用:

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

它测试用户输入是以"y"还是"y"开头,后跟零个或多个"es"。


确认很容易通过回车绕过,我发现持续提示有效输入很有用。

这是一个简单的功能。"如果未收到Y N,则"无效输入"显示为红色,并再次提示用户。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
\033[0m"
"invalid input"
    esac
  done  
}

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

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


这可能是一个黑客:

正如在UNIX/bash中所讨论的,"xargs-p"是在运行任何命令之前提示确认的一种好方法吗?

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

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

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

例子:

1
2
3
4
5
6
7
8
$ 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

$

将以下内容添加到/etc/bashrc文件中。这个脚本添加了一个常驻的"函数",而不是一个名为"确认"的别名。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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
}


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


好吧,这是我的版本的confirm,是从james'one改过来的:

1
2
3
4
5
6
7
8
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
}

这些变化包括:

  • 使用local防止变量名冲突
  • read使用$2 $3 ...来控制它的动作,所以你可以使用-n-t
  • 如果read退出不成功,echo就成了一条美丽的线路。
  • 我的Git on Windows只有bash-3.1,没有truefalse,所以使用return。当然,这也与bash-4.4兼容(当前的版本是Git for Windows)。
  • 使用ipython样式"(y/[n])"明确表示默认为"n"。

  • 尝试,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
     #!/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"

    不需要按Enter键

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

    • 返回0是,1
    • 不需要按Enter键-只需要一个字符
    • 可以按enter接受默认选项
    • 可以禁用默认选项以强制选择
    • 适用于zshbash

    按Enter时默认为"否"

    注意,N是大写的。在此按Enter,接受默认值:

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

    另请注意,[y/N]?是自动附加的。默认的"否"被接受,因此没有任何响应。

    重新提示直到给出有效响应:

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

    按Enter时默认为"是"

    注意,Y是资本化的:

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

    上面,我刚按下Enter,命令就运行了。

    enter无违约-需要YN

    1
    2
    3
    4
    5
    6
    $ 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]?中无资本化

    代码

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    # 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 '
    ' -> ''
      [[ $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 '

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

    游戏后期,但我又创造了另一个版本的confirm功能,以前的答案:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    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 $?
    }

    使用它:

    1
    confirm your_command

    特征:

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

    漏洞:

    • echo -enbash一起工作,但可能在你的外壳中失败。
    • 如果论据干扰echoxargs可能会失败。
    • 因为shell脚本编写很难


    不一样,但无论如何都有效的想法。

    1
    2
    3
    4
    5
    6
    7
    8
    #!/bin/bash  
    i='y'  
    while [ ${i:0:1} != n ]  
    do  
        # Command(s)  
        read -p" Again? Y/n" i  
        [[ ${#i} -eq 0 ]] && i='y'  
    done

    输出:再一次?y/n n再一次?任何东西再一次?Y/N 7再一次?Y/N/AMP;再一次?Y/N-NSIJF$

    现在只检查我读到的$1的第一个字符。


    这不是一个"问是还是不"的说法,而是一个黑客行为:把hg push ...化名为hgpushrepo,不是hgpushrepo,而是hgpushrepoconfirmedpush,当我能拼出整件事的时候,左脑做出了一个合乎逻辑的选择。


    下面的代码结合了两件事

  • Shopt-s NocaseMatch将处理不区分大小写的问题

  • 如果条件同时接受这两个输入,那么您要么通过Yes,Yes,Yes,Y。

    购物-S nocasemach

    如果[[SED-4.2.2.$line=~(是Y)$]

    然后退出0

    FI


  • 这是我使用本地化regex的解决方案。所以在德语中,"ja"的"j"也可以理解为"是"。

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

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    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]}
    }

    这是一个基本用法

    1
    2
    3
    4
    5
    6
    7
    # 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]? :"

    这可能有点太短了,但对我个人来说,它很管用

    1
    2
    3
    4
    5
    read -n 1 -p"Push master upstream? [Y/n]" reply;
    if ["$reply" !="" ]; then echo; fi
    if ["$reply" !="n" ]; then
        git push upstream master
    fi

    read -n 1只读取一个字符。如果不是小写字母"n",则假定为"y"。

    (至于真正的问题:创建一个bash脚本,并更改别名以指向该脚本,而不是指向以前的脚本)