¿Cómo solicito la entrada Sí/No/Cancelar en un script de shell de Linux?

Resuelto Myrddin Emrys asked hace 16 años • 39 respuestas

Quiero pausar la entrada en un script de shell y solicitar opciones al usuario.
La pregunta estándar Yes, Noo Canceltipo.
¿Cómo logro esto en un símbolo del sistema bash típico?

Myrddin Emrys avatar Oct 23 '08 00:10 Myrddin Emrys
Aceptado

Un método ampliamente disponible para obtener información del usuario en el símbolo del shell es el readcomando. Aquí hay una demostración:

while true; do
    read -p "Do you wish to install this program? " yn
    case $yn in
        [Yy]* ) make install; break;;
        [Nn]* ) exit;;
        * ) echo "Please answer yes or no.";;
    esac
done

Otro método, señalado por Steven Huwig , es el comando de Bash select. Aquí está el mismo ejemplo usando select:

echo "Do you wish to install this program?"
select yn in "Yes" "No"; do
    case $yn in
        Yes ) make install; break;;
        No ) exit;;
    esac
done

Con selectusted no necesita desinfectar la entrada: muestra las opciones disponibles y usted escribe un número correspondiente a su elección. También se repite automáticamente, por lo que no es necesario while truevolver a intentarlo si se proporciona una entrada no válida.

Además, Léa Gris demostró una forma de hacer que el lenguaje de solicitud sea independiente en su respuesta . Adaptar mi primer ejemplo para servir mejor en varios idiomas podría verse así:

set -- $(locale LC_MESSAGES)
yesexpr="$1"; noexpr="$2"; yesword="$3"; noword="$4"

while true; do
    read -p "Install (${yesword} / ${noword})? " yn
    if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi
    if [[ "$yn" =~ $noexpr ]]; then exit; fi
    echo "Answer ${yesword} / ${noword}."
done

Obviamente, aquí quedan otras cadenas de comunicación sin traducir (Instalar, Responder) que deberían abordarse en una traducción más completa, pero incluso una traducción parcial sería útil en muchos casos.

Finalmente, consulte la excelente respuesta de F. Hauri .

Myrddin Emrys avatar Oct 22 '2008 17:10 Myrddin Emrys

Al menos cinco respuestas para una pregunta genérica.

Dependiendo de

  • posixcompatible: podría funcionar en sistemas deficientes con genéricoscaparazónentornos
  • intentoespecífico: usar los llamados bashismos

y si tu quieres

  • pregunta/respuesta simple 'en línea' (soluciones genéricas)
  • interfaces bastante formateadas, comomaldicioneso más gráfico usando libgtk o libqt...
  • utilice la potente capacidad de leer el historial de líneas

1. Soluciones genéricas POSIX

Podrías usar el readcomando, seguido de if ... then ... else:

printf 'Is this a good question (y/n)? '
read answer

if [ "$answer" != "${answer#[Yy]}" ] ;then 
    echo Yes
else
    echo No
fi

(Gracias al comentario de Adam Katz : se reemplazó la prueba con la nueva anterior que es más portátil y evita una bifurcación :)

POSIX, pero característica clave única

Pero si no quieres que el usuario tenga que pulsar Return, puedes escribir:

( Editado: como sugiere acertadamente @JonathanLeffler , guardar la configuración de stty podría ser mejor que simplemente obligarlos a volverse cuerdos ).

printf 'Is this a good question (y/n)? '
old_stty_cfg=$(stty -g)
stty raw -echo ; answer=$(head -c 1) ; stty $old_stty_cfg # Careful playing with stty
if [ "$answer" != "${answer#[Yy]}" ];then
    echo Yes
else
    echo No
fi

Nota: Esto fue probado bajosh,intento,ksh,estrellarseycaja ocupada!

Lo mismo, pero esperando explícitamente yo n:

#/bin/sh
printf 'Is this a good question (y/n)? '
old_stty_cfg=$(stty -g)
stty raw -echo
answer=$( while ! head -c 1 | grep -i '[ny]' ;do true ;done )
stty $old_stty_cfg
if [ "$answer" != "${answer#[Yy]}" ];then
    echo Yes
else
    echo No
fi

Usando herramientas dedicadas

Hay muchas herramientas que se crearon utilizando , libncursesu otras bibliotecas gráficas. Por ejemplo, usando :libgtklibqtwhiptail

if whiptail --yesno "Is this a good question" 20 60 ;then
    echo Yes
else
    echo No
fi

Dependiendo de su sistema, es posible que deba reemplazarlo whiptailcon otra herramienta similar:

dialog --yesno "Is this a good question" 20 60 && echo Yes

gdialog --yesno "Is this a good question" 20 60 && echo Yes

kdialog --yesno "Is this a good question" 20 60 && echo Yes

donde 20es la altura del cuadro de diálogo en número de líneas y 60el ancho del cuadro de diálogo. Todas estas herramientas tienen casi la misma sintaxis.

DIALOG=whiptail
if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi
if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi
...
$DIALOG --yesno ...

2. Soluciones específicas de Bash

Método básico en línea

read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
    y|Y )
        echo Yes
    ;;
    * )
        echo No
    ;;
esac

Prefiero usarlo casepara poder realizar pruebas yes | ja | si | ouisi es necesario...

en línea con la característica clave única

En bash, podemos especificar la longitud de la entrada prevista para el readcomando:

read -n 1 -p "Is this a good question (y/n)? " answer

En bash, readel comando acepta un parámetro de tiempo de espera , que podría resultar útil.

read -t 3 -n 1 -p "Is this a good question (Y/n)? " answer
[ -z "$answer" ] && answer="Yes"  # if 'yes' have to be default choice

Tiempo de espera con cuenta atrás :

i=6 ;while ((i-->1)) &&
! read -sn 1 -t 1 -p $'\rIs this a good question (Y/n)? '$i$'..\e[3D' answer;do
  :;done ;[[ $answer == [nN] ]] && answer=No || answer=Yes ;echo "$answer "

3. Algunos trucos para herramientas dedicadas

Cuadros de diálogo más sofisticados, más allá de yes - nopropósitos simples:

dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe

Barra de progreso:

dialog --gauge "Filling the tank" 20 60 0 < <(
    for i in {1..100};do
        printf "XXX\n%d\n%(%a %b %T)T progress: %d\nXXX\n" $i -1 $i
        sleep .033
    done
) 

Pequeña demostración:

#!/bin/sh
while true ;do
    [ -x "$(which ${DIALOG%% *})" ] || DIALOG=dialog
    DIALOG=$($DIALOG --menu "Which tool for next run?" 20 60 12 2>&1 \
            whiptail       "dialog boxes from shell scripts" >/dev/tty \
            dialog         "dialog boxes from shell with ncurses" \
            gdialog        "dialog boxes from shell with Gtk" \
            kdialog        "dialog boxes from shell with Kde" ) || break
    clear;echo "Choosed: $DIALOG."
    for i in `seq 1 100`;do
        date +"`printf "XXX\n%d\n%%a %%b %%T progress: %d\nXXX\n" $i $i`"
        sleep .0125
      done | $DIALOG --gauge "Filling the tank" 20 60 0
    $DIALOG --infobox "This is a simple info box\n\nNo action required" 20 60
    sleep 3
    if $DIALOG --yesno  "Do you like this demo?" 20 60 ;then
        AnsYesNo=Yes; else AnsYesNo=No; fi
    AnsInput=$($DIALOG --inputbox "A text:" 20 60 "Text here..." 2>&1 >/dev/tty)
    AnsPass=$($DIALOG --passwordbox "A secret:" 20 60 "First..." 2>&1 >/dev/tty)
    $DIALOG --textbox /etc/motd 20 60
    AnsCkLst=$($DIALOG --checklist "Check some..." 20 60 12 \
        Correct "This demo is useful"        off \
        Fun        "This demo is nice"        off \
        Strong        "This demo is complex"        on 2>&1 >/dev/tty)
    AnsRadio=$($DIALOG --radiolist "I will:" 20 60 12 \
        " -1" "Downgrade this answer"        off \
        "  0" "Not do anything"                on \
        " +1" "Upgrade this anser"        off 2>&1 >/dev/tty)
    out="Your answers:\nLike: $AnsYesNo\nInput: $AnsInput\nSecret: $AnsPass"
    $DIALOG --msgbox "$out\nAttribs: $AnsCkLst\nNote: $AnsRadio" 20 60
  done

¿Más muestras? Eche un vistazo a Uso de Whiptail para elegir un dispositivo USB y un selector de almacenamiento USB extraíble: USBKeyChooser

5. Usando el historial de readline

Ejemplo:

#!/bin/bash

set -i
HISTFILE=~/.myscript.history
history -c
history -r

myread() {
    read -e -p '> ' $1
    history -s ${!1}
}
trap 'history -a;exit' 0 1 2 3 6

while myread line;do
    case ${line%% *} in
        exit )  break ;;
        *    )  echo "Doing something with '$line'" ;;
      esac
  done

Esto creará un archivo .myscript.historyen su $HOMEdirectorio, luego podrá usar los comandos del historial de Readline, como Up, Down, Ctrl+ ry otros.

F. Hauri - Give Up GitHub avatar Jan 10 '2015 10:01 F. Hauri - Give Up GitHub