In Bash, how to add “Are you sure [Y/n]” to any co

2019-01-12 14:19发布

In this particular case, I'd like to add a confirm in Bash for

Are you sure? [Y/n]

for Mercurial's hg push ssh://username@www.example.com//somepath/morepath, which is actually an alias. Is there a standard command that can be added to the alias to achieve it?

The reason is that hg push and hg out can sound similar and sometimes when I want hgoutrepo, I may accidentlly type hgpushrepo (both are aliases).

Update: if it can be something like a built-in command with another command, such as: confirm && hg push ssh://... that'd be great... just a command that can ask for a yes or no and continue with the rest if yes.

16条回答
Deceive 欺骗
2楼-- · 2019-01-12 14:37

Below code is combining two things

  1. shopt -s nocasematch that will take care of case insensitive

  2. and if condition that will accept both the input either you pass yes,Yes,YES,y.

    shopt -s nocasematch

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

    then exit 0

    fi

查看更多
Viruses.
3楼-- · 2019-01-12 14:42

Confirmations are easily bypassed with carriage returns, and I find it useful to continually prompt for valid input.

Here's a function to make this easy. "invalid input" appears in red if Y|N is not received, and the user is prompted again.

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

You can change the default prompt by passing an argument

查看更多
姐就是有狂的资本
4楼-- · 2019-01-12 14:43

These are more compact and versatile forms of Hamish's answer. They handle any mixture of upper and lower case letters:

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

Or, for 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

Note: If $response is an empty string, it will give an error. To fix, simply add quotation marks: "$response". – Always use double quotes in variables containing strings (e.g.: prefer to use "$@" instead $@).

Or, Bash 4.x:

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

Edit:

In response to your edit, here's how you'd create and use a confirm command based on the first version in my answer (it would work similarly with the other two):

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
}

To use this function:

confirm && hg push ssh://..

or

confirm "Would you really like to do a push?" && hg push ssh://..
查看更多
霸刀☆藐视天下
5楼-- · 2019-01-12 14:43

No pressing enter required

Here's a longer, but reusable and modular approach:

  • Returns 0=yes and 1=no
  • No pressing enter required - just a single character
  • Can press enter to accept the default choice
  • Can disable default choice to force a selection
  • Works for both zsh and bash.

Defaulting to "no" when pressing enter

Note that the N is capitalsed. Here enter is pressed, accepting the default:

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

Also note, that [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:

$ 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

$ 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

Here, 1 or false was returned. Note no capitalisation in [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
}
查看更多
霸刀☆藐视天下
6楼-- · 2019-01-12 14:46

This isn't exactly an "asking for yes or no" but just a hack: alias the hg push ... not to hgpushrepo but to hgpushrepoconfirmedpush and by the time I can spell out the whole thing, the left brain has made a logical choice.

查看更多
一纸荒年 Trace。
7楼-- · 2019-01-12 14:50

Try,

 #!/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"
查看更多
登录 后发表回答