In Bash, come aggiungere "Sei sicuro [S / N]" a qualsiasi comando o alias?


228

In questo caso particolare, vorrei aggiungere una conferma in Bash per

Sei sicuro? [Y / n]

per Mercurial hg push ssh://username@www.example.com//somepath/morepath, che in realtà è un alias. Esiste un comando standard che può essere aggiunto all'alias per ottenerlo?

Il motivo è che hg pushe hg outpuò sembrare simile e, a volte, quando voglio hgoutrepo, posso accidentalmente digitare hgpushrepo(entrambi sono alias).

Aggiornamento: se può essere qualcosa di simile a un comando integrato con un altro comando, come ad esempio: confirm && hg push ssh://...sarebbe fantastico ... solo un comando che può chiedere un yeso noe continuare con il resto se yes.


1
Probabilmente vorrai usare una funzione invece di un alias. Da info bash: "Per quasi tutti gli scopi, le funzioni della shell sono preferite agli alias."
In pausa fino a nuovo avviso.

veramente? anche per quelli come ls -lo rm -i?
polarità

5
Per quasi tutti, non tutti . A proposito, non fare mai qualcosa del genere alias rm='rm -i'. Un giorno, quando ne avrai più bisogno, l'alias non ci sarà e boom!qualcosa di importante andrà perso.
In pausa fino a nuovo avviso.

aspetta, se non hai l'alias per rm -i, non puoi contare anche su uno script di shell. Quindi scrivi sempre rm -iogni volta?
polarità

8
Riguarda le abitudini . Potresti creare un alias come quello nel mio commento precedente ed avere l'abitudine di digitare rme aspettarmi il -icomportamento, quindi un giorno l'alias non è lì (per qualche motivo viene disinserito o non impostato o sei su un sistema diverso ) e digiti rme procede immediatamente eliminando le cose senza conferma. Oops! Tuttavia, se avessi fatto un alias come alias askrm='rm -i'allora saresti a posto, dato che otterrai un errore "comando non trovato".
In pausa fino a nuovo avviso.

Risposte:


332

Queste sono forme più compatte e versatili della risposta di Hamish . Gestiscono qualsiasi combinazione di lettere maiuscole e minuscole:

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

Oppure, per Bash> = versione 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: se $responseè una stringa vuota, verrà visualizzato un errore. Per risolvere il problema, è sufficiente aggiungere le virgolette: "$response". - Usa sempre le virgolette doppie nelle variabili che contengono stringhe (es: preferisci usare "$@"invece $@).

Oppure, Bash 4.x:

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

Modificare:

In risposta alla tua modifica, ecco come creeresti e utilizzeresti un confirmcomando basato sulla prima versione della mia risposta (funzionerebbe in modo simile con gli altri due):

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
}

Per utilizzare questa funzione:

confirm && hg push ssh://..

o

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

9
In realtà dovrebbe essere [y / N] e non [Y / n] per il test corrente.
Simon A. Eugster,

Vale la pena ricordare che se si desidera verificare not equal toil secondo esempio, è necessario eseguire il cast della $responsevariabile. Ad esempio: if [[ ! $response =~ ^([yY][eE][sS]|[yY])$ ]].
João Cunha,

@ JoãoCunha: Sarebbe "non corrisponde" anziché "non uguale" poiché =~è l'operatore di corrispondenza regex.
In pausa fino a nuovo avviso.

@DennisWilliamson corretto, questo è quello che ho cercato di dire. Non riesco a modificare il mio commento per qualche motivo.
João Cunha,

6
Bella funzione sì / no per scopi generici . supporta un prompt forte e, facoltativamente, rende 'y' o 'n' predefinito.
sett.

19

Ecco circa un frammento che desideri. Vorrei scoprire come inoltrare gli argomenti.

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

Attenzione yes | command name here:)


13

Le conferme vengono facilmente aggirate con i ritorni a capo e trovo utile richiedere continuamente input validi.

Ecco una funzione per renderlo facile. "input non valido" appare in rosso se Y | N non viene ricevuto e l'utente viene nuovamente richiesto.

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

È possibile modificare il prompt predefinito passando un argomento


12

Per evitare di verificare esplicitamente queste varianti di 'yes', è possibile utilizzare l'operatore di espressione regolare bash '= ~' con un'espressione regolare:

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

Ciò verifica se l'input dell'utente inizia con 'y' o 'Y' ed è seguito da zero o più 'es.



4

Aggiungi quanto segue al tuo file / etc / bashrc. Questo script aggiunge una "funzione" residente invece di un alias chiamato "conferma".


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
}

Bello. Alcune correzioni minori, tuttavia: dovresti mettere tra virgolette doppie $responseed $@evitare errori, ci sono alcuni punti e virgola ridondanti e la *condizione dovrebbe tornare, non uscire.
Gordon Davisson,

Giusto per essere chiari, sono i singoli punti e virgola alla fine di alcune dichiarazioni che non sono necessari.
In pausa fino a nuovo avviso.

4

Questo potrebbe essere un po 'troppo breve, ma per il mio uso privato, funziona alla grande

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

Il read -n 1solo legge un personaggio. Non è necessario premere invio. Se non è una "n" o "N", si presume che sia una "Y". Basta premere Invio significa anche Y.

(per quanto riguarda la vera domanda: crea uno script bash e cambia il tuo alias in modo che punti a quello script invece di quello a cui puntava prima)


Breve e dolce, proprio quello di cui avevo bisogno. Grazie!
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

Quindi un personaggio spaziale significa sì?
Xen2050,

Tutti tranne "Sì o Sì" confermeranno l'azione, quindi hai ragione! @ xen2050
SergioAraujo,

3

Non è necessario premere invio richiesto

Ecco un approccio più lungo, ma riutilizzabile e modulare:

  • Restituisce 0= sì e 1= no
  • Non è necessario premere invio richiesto - solo un singolo carattere
  • Può premere enterper accettare la scelta predefinita
  • È possibile disabilitare la scelta predefinita per forzare una selezione
  • Funziona per entrambi zshe bash.

L'impostazione predefinita è "no" quando si preme invio

Si noti che Nè in maiuscolo. Qui viene premuto Invio, accettando il valore predefinito:

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

Si noti inoltre che è [y/N]?stato aggiunto automaticamente. Il "no" predefinito è accettato, quindi non viene ripetuto nulla.

Richiama finché non viene fornita una risposta valida:

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

L'impostazione predefinita è "sì" quando si preme invio

Nota che Yè in maiuscolo:

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

Sopra, ho appena premuto Invio, quindi il comando è stato eseguito.

Nessun valore predefinito attivo enter: richiede 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

Qui, 1o falso è stato restituito. Nota nessuna capitalizzazione in[y/n]?

Codice

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

2

Bene, ecco la mia versione di confirm, modificata da quella di 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
}

Queste modifiche sono:

  1. utilizzare localper impedire la collisione dei nomi delle variabili
  2. readutilizzare $2 $3 ...per controllare la sua azione, quindi è possibile utilizzare -ne-t
  3. se readesce senza successo, echouna linea si nutre di bellezza
  4. il mio Git on Windowsha solo bash-3.1e non ha trueo false, quindi usa returninvece. Naturalmente, questo è anche compatibile con bash-4.4 (quello attuale in Git per Windows).
  5. usa lo stile IPython "(y / [n])" per indicare chiaramente che "n" è l'impostazione predefinita.

2

Questa versione consente di avere più di un caso yo Y, noN

  1. Facoltativamente: ripetere la domanda fino a quando non viene fornita una domanda di approvazione

  2. Facoltativamente: ignora qualsiasi altra risposta

  3. Facoltativamente: uscire dal terminale, se lo si desidera

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

Notare anche questo: sull'ultima riga di questa funzione cancellare la variabile REPLY. Altrimenti, se echo $REPLYvedrai che è ancora impostato fino a quando non apri o chiudi il terminale o lo imposti nuovamente.


1

In ritardo al gioco, ma ho creato l'ennesima variante delle confirmfunzioni delle risposte precedenti:

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

Per usarlo:

confirm your_command

Caratteristiche:

  • stampa il comando come parte del prompt
  • passa gli argomenti usando il delimitatore NULL
  • conserva lo stato di uscita del comando

bugs:

  • echo -enfunziona con bashma potrebbe non riuscire nella shell
  • potrebbe non riuscire se gli argomenti interferiscono con echooxargs
  • molti miliardi di bug perché lo scripting della shell è difficile

1

Provare,

 #!/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"

0

Questo non è esattamente un "chiedere sì o no" ma solo un trucco: alias il hg push ...non farlo hgpushrepoma al momento hgpushrepoconfirmedpushe quando riesco a precisare il tutto, il cervello sinistro ha fatto una scelta logica.


1
Ma sai che userai la TABchiave!
Hamish Grubijan,

ok, vero, ma almeno darò un'occhiata al comando e vedrò che è strano e ci penso due volte prima di premere Invio
nonopolarità

0

Non è lo stesso, ma idea che funziona comunque.

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

Uscita: di
nuovo? S / n N
Di nuovo? S / n Qualcosa di
nuovo? S / n 7
Di nuovo? S / n e
ancora? S / n nsijf
$

Ora controlla solo il 1 ° carattere di $ ho letto.


0

Di seguito il codice sta combinando due cose

  1. shopas -s nocasematch che si prenderà cura di maiuscole e minuscole

  2. e se condizione che accetterà sia l'ingresso che si passa sì, Sì, SÌ, y.

    shopas -s nocasematch

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

    quindi esci da 0

    fi


0

Ecco la mia soluzione che utilizza regex localizzato. Quindi in tedesco anche "j" per "Ja" sarebbe interpretato come sì.

Il primo argomento è la domanda, se il secondo argomento è "y" di sì sarebbe la risposta predefinita, altrimenti no sarebbe la risposta predefinita. Il valore restituito è 0 se la risposta era "sì" e 1 se la risposta era "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]}
}

Ecco un uso di base

# 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]? : "
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.