Linux подтверждение выполнения команды

How do I prompt for Yes/No/Cancel input in a Linux shell script?

I want to pause input in a shell script, and prompt the user for choices.
The standard Yes , No , or Cancel type question.
How do I accomplish this in a typical bash prompt?

Just as a note: convention for prompts are such that if you present a [yn] option, the one that is capitalized is default, i.e. [Yn] defaults to «yes», and [yN] defaults to «no». See ux.stackexchange.com/a/40445/43532

You can also consider my related Q/A on U&L.SE about the canonical way to pause in bash . The provided results could easily be transferred.

37 Answers 37

The simplest and most widely available method to get user input at a shell prompt is the read command. The best way to illustrate its use is a simple 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 

Another method, pointed out by Steven Huwig, is Bash’s select command. Here is the same example using 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 

With select you don’t need to sanitize the input – it displays the available choices, and you type a number corresponding to your choice. It also loops automatically, so there’s no need for a while true loop to retry if they give invalid input.

Also, Léa Gris demonstrated a way to make the request language agnostic in her answer. Adapting my first example to better serve multiple languages might look like this:

set -- $(locale LC_MESSAGES) yesexpr="$1"; noexpr="$2"; yesword="$3"; noword="$4" while true; do read -p "Install ($ / $)? " yn if [[ "$yn" =~ $yesexpr ]]; then make install; exit; fi if [[ "$yn" =~ $noexpr ]]; then exit; fi echo "Answer $ / $." done 

Obviously other communication strings remain untranslated here (Install, Answer) which would need to be addressed in a more fully completed translation, but even a partial translation would be helpful in many cases.

Finally, please check out the excellent answer by F. Hauri.

Using Bash in OS X Leopard, I changed exit to break to keep from closing the tab when I selected ‘no’.

How does this work with options longer than Yes or No? In the case clause, do you write something like: Install program and do nothing afterwards ) make install; break;

Читайте также:  Astra linux openvpn server

@Shawn Using read you can, of course, use any glob or regex pattern supported by the bash shell’s switch statement. It just matches the text typed to your patterns until it finds a match. Using select, the list of choices is given to the command and it displays them to the user. You can have the items in the list be as long or as short as you like. I recommand checking their man pages for comprehensive usage information.

FWIW, I used this example to create a script that I intended to trigger via a remote SSH session. My SSH command looked like this: ssh my-server ‘path/to/myscript.sh’ . When executing this way, the prompt text for the read -p command does not show up. However, output from the echo command does. So for me, the better solution was to use echo -n «Do something? » followed by read yn .

At least five answers for one generic question.

  • posix compliant: could work on poor systems with generic shell environments
  • bash specific: using so called bashisms
  • simple ‘in line» question / answer (generic solutions)
  • pretty formatted interfaces, like ncurses or more graphical using libgtk or libqt.
  • use powerful readline history capability

1. POSIX generic solutions

You could use the read command, followed by if . then . else :

printf 'Is this a good question (y/n)? ' read answer if [ "$answer" != "$" ] ;then echo Yes else echo No fi 

(Thanks to Adam Katz’s comment: Replaced the test with the new one above that’s more portable and avoids one fork 🙂

POSIX, but single key feature

But if you don’t want the user to have to hit Return , you could write:

(Edited: As @JonathanLeffler rightly suggest, saving stty’s configuration could be better than simply force them to sane.)

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" != "$" ];then echo Yes else echo No fi 

Note: This was tested under sh, bash, ksh, dash and busybox!

Same, but waiting explicitly for y or 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" != "$" ];then echo Yes else echo No fi 

Using dedicated tools

There are many tools which were built using libncurses , libgtk , libqt or other graphical libraries. For example, using whiptail :

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

Depending on your system, you may need to replace whiptail with another similiar tool:

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 

where 20 is height of dialog box in number of lines and 60 is width of the dialog box. These tools all have near same 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 specific solutions

Basic in line method

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

I prefer to use case so I could even test for yes | ja | si | oui if needed.

Читайте также:  Тильда знак в линукс

in line with single key feature

Under bash, we can specify the length of intended input for for the read command:

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

Under bash, read command accepts a timeout parameter, which could be useful.

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 

Timeout with countdown:

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. Some tricks for dedicated tools

More sophisticated dialog boxes, beyond simple yes — no purposes:

dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe 
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 ) 
#!/bin/sh while true ;do [ -x "$(which $)" ] || 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 

5. Using readline’s history

#!/bin/bash set -i HISTFILE=~/.myscript.history history -c history -r myread() < read -e -p '>' $1 history -s $ > trap 'history -a;exit' 0 1 2 3 6 while myread line;do case $ in exit ) break ;; * ) echo "Doing something with '$line'" ;; esac done 

This will create a file .myscript.history in your $HOME directory, than you could use readline’s history commands, like Up , Down , Ctrl + r and others.

Источник

How do I prompt a user for confirmation in bash script? [duplicate]

I want to put a quick «are you sure?» prompt for confirmation at the top of a potentially dangerous bash script, what’s the easiest/best way to do this?

10 Answers 10

read -p "Are you sure? " -n 1 -r echo # (optional) move to a new line if [[ $REPLY =~ ^[Yy]$ ]] then # do dangerous stuff fi 

I incorporated levislevis85‘s suggestion (thanks!) and added the -n option to read to accept one character without the need to press Enter . You can use one or both of these.

Also, the negated form might look like this:

read -p "Are you sure? " -n 1 -r echo # (optional) move to a new line if [[ ! $REPLY =~ ^[Yy]$ ]] then [[ "$0" = "$BASH_SOURCE" ]] && exit 1 || return 1 # handle exits from shell or function but don't exit interactive shell fi 

However, as pointed out by Erich, under some circumstances such as a syntax error caused by the script being run in the wrong shell, the negated form could allow the script to continue to the «dangerous stuff». The failure mode should favor the safest outcome so only the first, non-negated if should be used.

Explanation:

The read command outputs the prompt ( -p «prompt» ) then accepts one character ( -n 1 ) and accepts backslashes literally ( -r ) (otherwise read would see the backslash as an escape and wait for a second character). The default variable for read to store the result in is $REPLY if you don’t supply a name like this: read -p «my prompt» -n 1 -r my_var

The if statement uses a regular expression to check if the character in $REPLY matches ( =~ ) an upper or lower case «Y». The regular expression used here says «a string starting ( ^ ) and consisting solely of one of a list of characters in a bracket expression ( [Yy] ) and ending ( $ )». The anchors ( ^ and $ ) prevent matching longer strings. In this case they help reinforce the one-character limit set in the read command.

The negated form uses the logical «not» operator ( ! ) to match ( =~ ) any character that is not «Y» or «y». An alternative way to express this is less readable and doesn’t as clearly express the intent in my opinion in this instance. However, this is what it would look like: if [[ $REPLY =~ ^[^Yy]$ ]]

Источник

Оцените статью
Adblock
detector