En Bash, ¿cómo agregar "¿Está seguro [S / n]" a cualquier comando o alias?

228

En este caso particular, me gustaría agregar una confirmación en Bash para

¿Estás seguro? [S / n]

para Mercurial hg push ssh://[email protected]//somepath/morepath, que en realidad es un alias. ¿Existe un comando estándar que se pueda agregar al alias para lograrlo?

La razón es que hg push, y hg outpueden parecer similares y, a veces, cuando quiero hgoutrepo, puedo escribir accidentlly hgpushrepo(ambos son alias).

Actualización: si puede ser algo así como un comando incorporado con otro comando, como: confirm && hg push ssh://...eso sería genial ... solo un comando que puede solicitar un yeso noy continuar con el resto si yes.

nonopolaridad
fuente
1
Probablemente desee utilizar una función en lugar de un alias. De info bash: "Para casi cualquier propósito, las funciones de shell son preferibles a los alias".
Pausado hasta nuevo aviso.
¿De Verdad? incluso para unos como ls -lo rm -i?
nonopolaridad
55
Para casi todos, no para todos . Por cierto, nunca hagas algo así alias rm='rm -i'. Un día, cuando más lo necesites, el alias no estará allí y boom!se perderá algo importante.
Pausado hasta nuevo aviso.
espere, si no tiene el alias rm -i, entonces no puede contar con tener un script de shell también. Entonces, ¿siempre escribes rm -itodo el tiempo?
nonopolaridad
8
Se trata de hábitos . Puede crear un alias como el de mi comentario anterior y tener el hábito de escribir rmy esperar el -icomportamiento, luego, un día, el alias no está allí (por alguna razón se desarma o no se configura o está en un sistema diferente ) y escribe rmy continúa inmediatamente eliminando cosas sin confirmación. ¡Uy! Sin embargo, si hizo un alias como alias askrm='rm -i'entonces estaría bien, ya que obtendría un error de "comando no encontrado".
Pausado hasta nuevo aviso.

Respuestas:

332

Estas son formas más compactas y versátiles de la respuesta de Hamish . Manejan cualquier combinación de letras mayúsculas y minúsculas:

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

O, para Bash> = versión 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

Nota: Si $responsees una cadena vacía, dará un error. Para solucionarlo, basta con añadir las comillas: "$response". - Utilice siempre comillas dobles en variables que contengan cadenas (por ejemplo: prefiera usar "$@"en su lugar$@ ).

O, Bash 4.x:

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

Editar:

En respuesta a su edición, así es como crearía y usaría un confirmcomando basado en la primera versión en mi respuesta (funcionaría de manera similar con los otros dos):

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
}

Para usar esta función:

confirm && hg push ssh://..

o

confirm "Would you really like to do a push?" && hg push ssh://..
Pausado hasta nuevo aviso.
fuente
99
En realidad, debería ser [y / N] y no [Y / n] para la prueba actual.
Simon A. Eugster
Vale la pena mencionar que si desea verificar not equal toel segundo ejemplo, debe convertir la $responsevariable. Por ejemplo: if [[ ! $response =~ ^([yY][eE][sS]|[yY])$ ]].
João Cunha
@ JoãoCunha: Eso sería "no coincidente" en lugar de "no igual", ya que =~es el operador de coincidencia de expresiones regulares.
Pausado hasta nuevo aviso.
@DennisWilliamson correcto, eso es lo que traté de decir. No puedo editar mi comentario por alguna razón.
João Cunha
66
Buen propósito general sí / no función . admite un aviso fuerte y, opcionalmente, hacer que 'y' o 'n' sean predeterminados.
wkw
19

Aquí hay más o menos un fragmento que desea. Déjame descubrir cómo reenviar los argumentos.

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

Cuidado con yes | command name here:)

Hamish Grubijan
fuente
14

Las confirmaciones se omiten fácilmente con los retornos de carro, y me resulta útil solicitar continuamente una entrada válida.

Aquí hay una función para facilitar esto. "entrada no válida" aparece en rojo si Y | N no se recibe, y se le solicita al usuario nuevamente.

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

Puede cambiar la solicitud predeterminada pasando un argumento

Briceburg
fuente
12

Para evitar comprobar explícitamente estas variantes de 'sí', puede usar el operador de expresión regular bash '= ~' con una expresión regular:

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

Eso prueba si la entrada del usuario comienza con 'y' o 'Y' y es seguida por cero o más 'es's.

usuario389850
fuente
5

Esto puede ser un truco:

como en cuestión En Unix / Bash, ¿es "xargs -p" una buena forma de solicitar confirmación antes de ejecutar cualquier comando?

podemos usar xargspara hacer el trabajo:

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

por supuesto, esto se establecerá como un alias, como hgpushrepo

Ejemplo:

$ 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

$
nonopolaridad
fuente
4

Agregue lo siguiente a su archivo / etc / bashrc. Este script agrega una "función" residente en lugar de un alias llamado "confirmar".


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
}
MattyV
fuente
Agradable. Sin embargo, algunas correcciones menores: debe poner entre comillas dobles $responsey $@para evitar errores de análisis, hay algunos puntos y comas redundantes, y la *condición debe regresar, no salir.
Gordon Davisson
Para ser claros, son los puntos y comas al final de algunas declaraciones que son innecesarios.
Pausado hasta nuevo aviso.
4

Esto puede ser un poco corto, pero para mi uso privado, funciona muy bien

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

El read -n 1solo lee un personaje. No es necesario presionar enter. Si no es una 'n' o 'N', se supone que es una 'Y'. Simplemente presionando enter significa Y también.

(en cuanto a la pregunta real: haga que sea un script bash y cambie su alias para que apunte a ese script en lugar de lo que apuntaba antes)

Pico común
fuente
Corto y dulce, justo lo que necesitaba. ¡Gracias!
Raymo111
3
read -r -p "Are you sure? [Y/n]" response
  response=${response,,} # tolower
  if [[ $response =~ ^(yes|y| ) ]] || [[ -z $response ]]; then
      your-action-here
  fi
SergioAraujo
fuente
¿Entonces un personaje espacial significa que sí?
Xen2050
Todos menos 'sí o y' confirmarán la acción, ¡así que tienes razón! @ xen2050
SergioAraujo
3

No presionar enter requerido

Aquí hay un enfoque más largo, pero reutilizable y modular:

  • Devuelve 0= sí y 1= no
  • No es necesario presionar enter, solo un carácter
  • Puede presionar enterpara aceptar la opción predeterminada
  • Puede deshabilitar la opción predeterminada para forzar una selección
  • Funciona para ambos zshy bash.

Por defecto a "no" al presionar enter

Tenga en cuenta que el Nestá en mayúscula. Aquí se presiona enter, aceptando el valor predeterminado:

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

También tenga en cuenta que eso [y/N]?se agregó automáticamente. Se acepta el "no" predeterminado, por lo que no se repite nada.

Vuelva a preguntar hasta que se dé una respuesta válida:

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

Por defecto a "sí" al presionar enter

Tenga en cuenta que Yse escribe con mayúscula:

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

Arriba, solo presioné enter, por lo que el comando se ejecutó.

Sin valor predeterminado activado enter: requiere yon

$ 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

Aquí, 1o falso fue devuelto. Tenga en cuenta que no hay mayúsculas en[y/n]?

Código

# 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
}
Tom Hale
fuente
2

Bueno, aquí está mi versión de confirm, modificada de la de 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
}

Estos cambios son:

  1. se usa localpara evitar que los nombres de variables choquen
  2. readuse $2 $3 ...para controlar su acción, por lo que puede usar -ny-t
  3. si readsale sin éxito, echoun salto de línea para belleza
  4. mi Git on Windowsúnico tiene bash-3.1y no tiene trueo false, así que usereturn en lugar. Por supuesto, esto también es compatible con bash-4.4 (el actual en Git para Windows).
  5. use el estilo IPython "(y / [n])" para indicar claramente que "n" es el valor predeterminado.
Dahan Gong
fuente
2

Esta versión le permite tener más de un caso yo Y, noN

  1. Opcionalmente: repita la pregunta hasta que se proporcione una pregunta de aprobación

  2. Opcionalmente: ignore cualquier otra respuesta

  3. Opcionalmente: salga de la terminal si lo desea

    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=''
    }

Observe esto también: en la última línea de esta función, borre la variable REPLY. De lo contrario, si echo $REPLYverá, todavía está configurado hasta que abra o cierre su terminal o lo vuelva a configurar.

jasonleonhard
fuente
1

Tarde al juego, pero creé otra variante de las confirmfunciones de las respuestas anteriores:

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 $?
}

Para usarlo:

confirm your_command

caracteristicas:

  • imprime su comando como parte de la solicitud
  • pasa argumentos utilizando el delimitador NULL
  • conserva el estado de salida de su comando

Loco:

  • echo -enfunciona bashpero puede fallar en su shell
  • podría fallar si los argumentos interfieren con echooxargs
  • un montón de otros errores porque las secuencias de comandos de shell son difíciles
thomas.me
fuente
1

Tratar,

 #!/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"
Ranjithkumar T
fuente
0

Esto no es exactamente un "pedir sí o no", sino solo un truco: alias the but to hg push ...to hgpushrepoto hgpushrepoconfirmedpushy para cuando pueda explicar todo, el cerebro izquierdo ha tomado una decisión lógica.

nonopolaridad
fuente
1
¡Pero sabes que usarás la TABllave!
Hamish Grubijan
ok, es cierto, pero al menos echaré un vistazo al comando y veré que es extraño y lo pensaré dos veces antes de
presionar
0

No es lo mismo, pero idea que funciona de todos modos.

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

Salida:
¿otra vez? S / n N
otra vez? S / n ¿Algo
nuevo? S / n 7
otra vez? S / n &
otra vez? S / n nsijf
$

Ahora solo verifica el 1er carácter de $ i read.

anónimo
fuente
0

El siguiente código combina dos cosas

  1. shopt -s nocasematch que se ocupará de mayúsculas y minúsculas

  2. y si la condición que aceptará tanto la entrada pasa sí, sí, sí, y.

    shopt -s nocasematch

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

    luego salga 0

    fi

Shyam Gupta
fuente
0

Aquí está mi solución que usando expresiones regulares localizadas. Así que en alemán también "j" para "Ja" se interpretaría como sí.

El primer argumento es la pregunta, si el segundo argumento es "y", sí sería la respuesta predeterminada; de lo contrario, no sería la respuesta predeterminada. El valor de retorno es 0 si la respuesta fue "sí" y 1 si la respuesta fue "no".

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]}
}

Aquí hay un uso básico

# 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]? : "
David Boho
fuente