linux - template - shell script exit




Wie fordere ich zur Eingabe von Ja/Nein/Abbrechen in einem Linux-Shell-Skript auf? (17)

Ich möchte die Eingabe in einem Shell-Skript anhalten und den Benutzer zur Auswahl auffordern. Die Standardfrage "Ja, Nein oder Abbrechen". Wie erreiche ich dies in einer typischen Bash-Eingabeaufforderung?


Ja / Nein / Abbrechen

Funktion

#!/usr/bin/env bash
@confirm() {
  local message="$*"
  local result=''

  echo -n "> $message (Yes/No/Cancel) " >&2

  while [ -z "$result" ] ; do
    read -s -n 1 choice
    case "$choice" in
      y|Y ) result='Y' ;;
      n|N ) result='N' ;;
      c|C ) result='C' ;;
    esac
  done

  echo $result
}

Verwendung

case $(@confirm 'Confirm?') in
  Y ) echo "Yes" ;;
  N ) echo "No" ;;
  C ) echo "Cancel" ;;
esac

Bestätigen Sie mit einer sauberen Benutzereingabe

Funktion

#!/usr/bin/env bash
@confirm() {
  local message="$*"
  local result=3

  echo -n "> $message (y/n) " >&2

  while [[ $result -gt 1 ]] ; do
    read -s -n 1 choice
    case "$choice" in
      y|Y ) result=0 ;;
      n|N ) result=1 ;;
    esac
  done

  return $result
}

Verwendung

if @confirm 'Confirm?' ; then
  echo "Yes"
else
  echo "No"
fi

Mindestens fünf Antworten für eine generische Frage.

Es hängt davon ab

  • posix compliant: könnte auf schlechten Systemen mit generischen shell Umgebungen funktionieren
  • bash spezifisch: Verwenden von sogenannten Bashismen

und wenn du willst

  • einfache "in line" Frage / Antwort (generische Lösungen)
  • ziemlich formatierte Schnittstellen wie ncurses oder mehr, die mit libgtk oder libqt grafisch sind ...
  • Verwenden Sie leistungsstarke Readline History-Funktionen

1. POSIX generische Lösungen

Sie könnten den read , gefolgt von if ... then ... else :

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

# if echo "$answer" | grep -iq "^y" ;then

# (Dank Adam Katz Kommentar : Dieser Test ist tragbarer und vermeiden Sie eine Gabel :)

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

POSIX, aber Single-Key-Funktion

Wenn Sie jedoch nicht möchten, dass der Benutzer Return drückt , könnten Sie schreiben:

( Bearbeitet: Wie @ JonathanLeffler zu Recht vorschlägt, könnte es besser sein, die Konfiguration von stty zu speichern , als sie einfach zu normal zu machen .)

echo -n "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 echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

Hinweis: Dies wurde unter sh , bash , ksh , dash und busybox !

Gleich, aber warte explizit auf y oder n :

#/bin/sh
echo -n "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 echo "$answer" | grep -iq "^y" ;then
    echo Yes
else
    echo No
fi

Verwenden von dedizierten Tools

Es gibt viele Tools, die mit libncurses , libgtk , libqt oder anderen grafischen Bibliotheken erstellt wurden. Zum Beispiel mit whiptail :

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

Abhängig von Ihrem System müssen Sie whiptail möglicherweise durch ein anderes ähnliches Werkzeug ersetzen:

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

Dabei steht 20 für die Höhe des Dialogfelds in der Anzahl der Zeilen und 60 für die Breite des Dialogfelds. Diese Werkzeuge haben alle nahezu die gleiche Syntax.

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

2. Bash spezifische Lösungen

Grundlegende Inline- Methode

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

Ich ziehe es vor, den case zu verwenden case so dass ich sogar auf yes | ja | si | oui testen könnte yes | ja | si | oui yes | ja | si | oui wenn nötig ...

in Übereinstimmung mit einzelnen Schlüsselmerkmal

Unter bash können wir die Länge der beabsichtigten Eingabe für den Lesebefehl angeben:

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

Unter bash akzeptiert der Lesebefehl einen Timeout- Parameter, der nützlich sein könnte.

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

Einige Tricks für dedizierte Werkzeuge

Anspruchsvollere Dialogfelder, über einfache yes - no Zwecke hinaus:

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

Fortschrittsanzeige:

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
) 

Kleine Demo:

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

5. Verwendung der Geschichte von readline

Beispiel:

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

Dadurch wird eine Datei .myscript.history in Ihrem $HOME Verzeichnis erstellt, als Sie die readline-History-Befehle wie Up , Down , Ctrl + r und andere verwenden könnten.


Als Freund eines Ein-Zeilen-Befehls habe ich folgendes benutzt:

while [ -z $prompt ]; do read -p "Continue (y/n)?" choice;case "$choice" in y|Y ) prompt=true; break;; n|N ) exit 0;; esac; done; prompt=;

Schriftliche Longform, das funktioniert so:

while [ -z $prompt ];
  do read -p "Continue (y/n)?" choice;
  case "$choice" in
    y|Y ) prompt=true; break;;
    n|N ) exit 0;;
  esac;
done;
prompt=;

Bash hat zu diesem Zweck ausgewählt.

select result in Yes No Cancel
do
    echo $result
done

Diese Lösung liest ein einzelnes Zeichen und ruft eine Funktion auf eine Ja-Antwort auf.

read -p "Are you sure? (y/n) " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
    do_something      
fi

Ein einfacher Weg dies zu tun ist mit xargs -p oder parallel --interactive .

Ich mag das Verhalten von xargs ein wenig besser dafür, weil es jeden Befehl direkt nach der Eingabeaufforderung wie andere interaktive unix-Befehle ausführt, anstatt die yesses zu sammeln, um am Ende zu laufen. (Sie können Strg-C drücken, nachdem Sie die gewünschten Ergebnisse erreicht haben.)

z.B,

echo *.xml | xargs -p -n 1 -J {} mv {} backup/

Hier ist etwas, was ich zusammengestellt habe:

#!/bin/sh

promptyn () {
    while true; do
        read -p "$1 " yn
        case $yn in
            [Yy]* ) return 0;;
            [Nn]* ) return 1;;
            * ) echo "Please answer yes or no.";;
        esac
    done
}

if promptyn "is the sky blue?"; then
    echo "yes"
else
    echo "no"
fi

Ich bin ein Anfänger, also nimm das mit einem Körnchen Salz, aber es scheint zu funktionieren.


Ich habe die case Anweisung in einem solchen Szenario ein paar Mal verwendet, die Verwendung der Case-Anweisung ist ein guter Weg, um darüber zu gehen. Eine while Schleife, die den Fallblock kapselt, die eine boolesche Bedingung verwendet, kann implementiert werden, um noch mehr Kontrolle über das Programm zu behalten und viele andere Anforderungen zu erfüllen. Nachdem alle Bedingungen erfüllt sind, kann eine break verwendet werden, die die Kontrolle an den Hauptteil des Programms zurückgibt. Um andere Bedingungen zu erfüllen, können natürlich bedingte Anweisungen hinzugefügt werden, um die Kontrollstrukturen zu begleiten: case Anweisung und mögliche while Schleife.

Beispiel für die Verwendung einer Fallanweisung zur Erfüllung Ihrer Anfrage

#! /bin/sh 

# For potential users of BSD, or other systems who do not
# have a bash binary located in /bin the script will be directed to
# a bourne-shell, e.g. /bin/sh

# NOTE: It would seem best for handling user entry errors or
# exceptions, to put the decision required by the input 
# of the prompt in a case statement (case control structure), 

echo Would you like us to perform the option: "(Y|N)"

read inPut

case $inPut in
    # echoing a command encapsulated by 
    # backticks (``) executes the command
    "Y") echo `Do something crazy`
    ;;
    # depending on the scenario, execute the other option
    # or leave as default
    "N") echo `execute another option`
    ;;
esac

exit

Inspiriert von den Antworten von @Mark und @Myrddin habe ich diese Funktion für eine universelle Eingabe erstellt

uniprompt(){
    while true; do
        echo -e "$1\c"
        read opt
        array=($2)
        case "${array[@]}" in  *"$opt"*) eval "$3=$opt";return 0;; esac
        echo -e "$opt is not a correct value\n"
    done
}

benutze es so:

unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
echo "$selection"

Mir ist aufgefallen, dass niemand eine Antwort gepostet hat, die ein mehrzeiliges Echo-Menü für solch einfache Benutzereingaben zeigt. Hier ist mein Beispiel:

#!/bin/bash

function ask_user() {    

echo -e "
#~~~~~~~~~~~~#
| 1.) Yes    |
| 2.) No     |
| 3.) Quit   |
#~~~~~~~~~~~~#\n"

read -e -p "Select 1: " choice

if [ "$choice" == "1" ]; then

    do_something

elif [ "$choice" == "2" ]; then

    do_something_else

elif [ "$choice" == "3" ]; then

    clear && exit 0

else

    echo "Please select 1, 2, or 3." && sleep 3
    clear && ask_user

fi
}

ask_user

Diese Methode wurde in der Hoffnung veröffentlicht, dass jemand sie nützlich und zeitsparend finden könnte.


Sie können den integrierten Lesebefehl verwenden. Verwenden Sie die Option -p , um den Benutzer mit einer Frage zu fragen.

Seit BASH4 können Sie nun -i , um eine Antwort vorzuschlagen, so dass der Benutzer nur return drücken muss, um ihn einzugeben:

read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH

(Aber denken Sie daran, die "readline" -Option zu verwenden, um die Zeilenbearbeitung mit den Pfeiltasten zu ermöglichen)


Um ein nettes ncurses-ähnliches Eingabefeld zu erhalten, benutzen Sie den Befehlsdialog wie folgt:

#!/bin/bash
if (dialog --title "Message" --yesno "Want to do something risky?" 6 25)
# message box will have the size 25x6 characters
then 
    echo "Let's do something risky"
    # do something risky
else 
    echo "Let's stay boring"
fi

Das Dialogpaket wird standardmäßig mindestens mit SUSE Linux installiert.


generischer wäre:

function menu(){
    title="Question time"
    prompt="Select:"
    options=("Yes" "No" "Maybe")
    echo "$title"
    PS3="$prompt"
    select opt in "${options[@]}" "Quit/Cancel"; do
        case "$REPLY" in
            1 ) echo "You picked $opt which is option $REPLY";;
            2 ) echo "You picked $opt which is option $REPLY";;
            3 ) echo "You picked $opt which is option $REPLY";;
            $(( ${#options[@]}+1 )) ) clear; echo "Goodbye!"; exit;;
            *) echo "Invalid option. Try another one.";continue;;
         esac
     done
     return
}

Die einfachste und am weitesten verbreitete Methode, Benutzereingaben an einer Shell-Eingabeaufforderung zu erhalten, ist der read . Der beste Weg, seine Verwendung zu veranschaulichen, ist eine einfache Demonstration:

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

Eine andere Methode, auf die Steven Huwig hingewiesen hat, ist Bashs select Befehl. Hier ist das selbe Beispiel mit 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

Bei select Sie die Eingabe nicht bereinigen - sie zeigt die verfügbaren Auswahlmöglichkeiten an und Sie geben eine Nummer ein, die Ihrer Auswahl entspricht. Es wird auch automatisch eine Schleife durchlaufen, so dass es nicht nötig ist, eine while true Schleife zu wiederholen, wenn sie eine ungültige Eingabe liefert.

Schauen Sie sich auch die ausgezeichnete Antwort von F. Hauri an.


inquire ()  {
  echo  -n "$1 [y/n]? "
  read answer
  finish="-1"
  while [ "$finish" = '-1' ]
  do
    finish="1"
    if [ "$answer" = '' ];
    then
      answer=""
    else
      case $answer in
        y | Y | yes | YES ) answer="y";;
        n | N | no | NO ) answer="n";;
        *) finish="-1";
           echo -n 'Invalid response -- please reenter:';
           read answer;;
       esac
    fi
  done
}

... other stuff

inquire "Install now?"

...

read -e -p "Enter your choice: " choice

Mit der Option -e kann der Benutzer die Eingabe mit den Pfeiltasten bearbeiten.

Wenn Sie einen Vorschlag als Eingabe verwenden möchten:

read -e -i "yes" -p "Enter your choice: " choice

-i Option druckt eine suggestive Eingabe.


yn() {
  if [[ 'y' == `read -s -n 1 -p "[y/n]: " Y; echo $Y` ]];
  then eval $1;
  else eval $2;
  fi }
yn 'echo yes' 'echo no'
yn 'echo absent no function works too!'




scripting