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?
我想暂停shell脚本中的输入,并提示用户进行选择。标准的“是、否或取消”类型问题。如何在典型的bash提示符中实现这一点?
26 个解决方案
#1
1249
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:
在shell提示符下获取用户输入的最简单、最广泛的方法是read命令。说明其使用的最佳方法是简单的演示:
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
:
Steven Huwig指出的另一个方法是Bash的select命令。下面是使用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.
使用select,您不需要对输入进行消毒——它将显示可用的选项,然后您键入一个与您的选择相对应的数字。它也会自动循环,所以如果它们给出无效的输入,就不需要一个while循环来重试。
Also, please check out the excellent answer by F. Hauri.
另外,请查看F. Hauri的精彩答案。
#2
363
At least five answers for one generic question.
Depending on
根据
- posix compliant: could work on poor systems with generic shell environments
- posix兼容:可以在具有通用shell环境的糟糕系统上工作
- bash specific: using so called bashisms
- bash专用:使用所谓的bashisms。
and if you want
如果你想
- simple ``in line'' question / answer (generic solutions)
- 简单的“in line”问题/答案(通用解决方案)
- pretty formatted interfaces, like ncurses or more graphical using libgtk or libqt...
- 漂亮的格式化界面,比如ncurses或者使用libgtk或libqt的图形化界面……
- use powerful readline history capability
- 使用强大的读线历史功能
1. POSIX generic solutions
You could use the read
command, followed by if ... then ... else
:
您可以使用read命令,然后是if…然后……其他:
echo -n "Is this a good question (y/n)? "
read answer
# if echo "$answer" | grep -iq "^y" ;then
# (Thanks to Adam Katz's comment: This test is more portable and avoid one fork:)
#(感谢Adam Katz的评论:这个测试更便携,并且避免使用一个fork:)
if [ "$answer" != "${answer#[Yy]}" ] ;then
echo Yes
else
echo No
fi
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.)
(编辑:正如@JonathanLeffler正确地指出的那样,保存stty的配置可能比简单地强迫他们保持清醒要好。)
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
Note: This was tested under sh, bash, ksh, dash and busybox!
注意:这是在sh, bash, ksh, dash和busybox下测试的!
Same, but waiting explicitly for y or n:
同样,但明确地等待y或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
Using dedicated tools
There are many tools which were built using libncurses
, libgtk
, libqt
or other graphical libraries. For example, using whiptail
:
有许多使用libncurses、libgtk、libqt或其他图形库构建的工具。例如,使用鞭尾:
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:
根据您的系统,您可能需要用另一个类似工具替换whiptail:
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.
其中20为对话框的高度(行数),60为对话框的宽度。这些工具都具有几乎相同的语法。
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 ${answer:0:1} 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...
我更喜欢用例,这样我甚至可以测试yes | ja |如果需要的话,|是的…
in line with single key feature
Under bash, we can specify the length of intended input for for the read
command:
在bash中,我们可以为read命令指定预期输入的长度:
read -n 1 -p "Is this a good question (y/n)? " answer
Under bash, read
command accepts a timeout parameter, which could be useful.
在bash中,read命令接受一个超时参数,这可能很有用。
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
Some tricks for dedicated tools
More sophisticated dialog boxes, beyond simple yes - no
purposes:
更复杂的对话框,除了简单的yes—no purpose:
dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
Progress bar:
进度条:
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
)
Little 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
More sample? Have a look at Using whiptail for choosing USB device and USB removable storage selector: USBKeyChooser
更多的样本吗?看看使用whiptail选择USB设备和USB移动存储选择器:USBKeyChooser
5. Using readline's history
Example:
例子:
#!/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
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.
这将创建一个文件。myscript。在$HOME目录中的历史,比使用readline的历史命令要多,比如向上、向下、Ctrl+r等。
#3
332
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"
#4
115
You can use the built-in read command ; Use the -p
option to prompt the user with a question.
可以使用内置的read命令;使用-p选项提示用户提出问题。
Since BASH4, you can now use -i
to suggest an answer, so the user only have to press return
to enter it :
由于BASH4现在可以使用-i来建议答案,因此用户只需按return即可进入:
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH
(But remember to use the "readline" option -e
to allow line editing with arrow keys)
(但请记住使用“readline”选项-e允许使用箭头键进行行编辑)
#5
92
Bash has select for this purpose.
Bash已经为此选择了。
select result in Yes No Cancel
do
echo $result
done
#6
51
read -p "Are you alright? (y/n) " RESP
if [ "$RESP" = "y" ]; then
echo "Glad to hear it"
else
echo "You need more bash programming"
fi
#7
27
Here's something I put together:
这是我整理的东西:
#!/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
I'm a beginner, so take this with a grain of salt, but it seems to work.
我是一个初学者,所以对此持怀疑态度,但这似乎行得通。
#8
22
You want:
- Bash builtin commands (i.e. portable)
- Bash内置命令(即可移植)
- Check TTY
- 检查遥控
- Default answer
- 默认的答案
- Timeout
- 超时
- Colored question
- 颜色的问题
Snippet
do_xxxx=y # In batch mode => Default is Yes
[[ -t 0 ]] && # If TTY => Prompt the question
read -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx # Store the answer in $do_xxxx
if [[ $do_xxxx =~ ^(y|Y|)$ ]] # Do if 'y' or 'Y' or empty
then
xxxx
fi
Explanations
-
[[ -t 0 ]] && read ...
=> Call commandread
if TTY - [- t0] && & read…=>调用命令读取if TTY
-
read -n 1
=> Wait for one character - 读取- n1 =>等待一个字符
-
$'\e[1;32m ... \e[0m '
=> Print in green
(green is fine because readable on both white/black backgrounds) - “\ e[1;32美元……\e[0m ' =>绿色打印(绿色可以,因为白色/黑色背景都可以阅读)
-
[[ $do_xxxx =~ ^(y|Y|)$ ]]
=> bash regex - [[$ do_xxxx = ~ ^(y y | |)美元]]= > bash正则表达式
Timeout => Default answer is No
do_xxxx=y
[[ -t 0 ]] && { # Timeout 5 seconds (read -t 5)
read -t 5 -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx || # read 'fails' on timeout
do_xxxx=n ; } # Timeout => answer No
if [[ $do_xxxx =~ ^(y|Y|)$ ]]
then
xxxx
fi
#9
20
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?"
...
#10
19
The easiest way to achieve this with the least number of lines is as follows:
用最少的行数来实现这个最简单的方法是:
read -p "<Your Friendly Message here> : y/n/cancel" CONDITION;
if [ "$CONDITION" == "y" ]; then
# do something here!
fi
The if
is just an example: it is up to you how to handle this variable.
if只是一个例子:如何处理这个变量取决于您。
#11
15
This solution reads a single character and calls a function on a yes response.
该解决方案读取单个字符并在yes响应上调用函数。
read -p "Are you sure? (y/n) " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
do_something
fi
#12
15
Use the read
command:
使用读取命令:
echo Would you like to install? "(Y or N)"
read x
# now check if $x is "y"
if [ "$x" = "y" ]; then
# do something here!
fi
and then all of the other stuff you need
然后你需要的其他东西。
#13
12
read -e -p "Enter your choice: " choice
The -e
option enables the user to edit the input using arrow keys.
e选项允许用户使用箭头键编辑输入。
If you want to use a suggestion as input:
如果你想用一个建议作为输入:
read -e -i "yes" -p "Enter your choice: " choice
-i
option prints a suggestive input.
-i选项打印提示输入。
#14
7
Sorry for posting on such an old post. Some weeks ago I was facing a similar problem, in my case I needed a solution which also worked within an online installer-script, eg: curl -Ss https://raw.github.com/_____/installer.sh | bash
很抱歉在这么旧的帖子上发帖。几个星期前,我遇到了一个类似的问题,在我的例子中,我需要一个同样适用于在线安装脚本的解决方案,例如:curl -Ss https://raw.github.com/_____/installer.sh | bash
Using read yesno < /dev/tty
works fine for me:
使用read yesno < /dev/tty对我来说合适:
echo -n "These files will be uploaded. Is this ok? (y/n) "
read yesno < /dev/tty
if [ "x$yesno" = "xy" ];then
# Yes
else
# No
fi
Hope this helps someone.
希望这可以帮助别人。
#15
5
To get a nice ncurses-like inputbox use the command dialog like this:
要获得一个类似ncurses的输入框,请使用如下所示的命令对话框:
#!/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
The dialog package is installed by default at least with SUSE Linux.
至少在SUSE Linux中,对话框包是默认安装的。
#16
3
Multiple choice version:
多项选择版本:
ask () { # $1=question $2=options
# set REPLY
# options: x=..|y=..
while $(true); do
printf '%s [%s] ' "$1" "$2"
stty cbreak
REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
stty -cbreak
test "$REPLY" != "$(printf '\n')" && printf '\n'
(
IFS='|'
for o in $2; do
if [ "$REPLY" = "${o%%=*}" ]; then
printf '\n'
break
fi
done
) | grep ^ > /dev/null && return
done
}
Example:
例子:
$ ask 'continue?' 'y=yes|n=no|m=maybe'
continue? [y=yes|n=no|m=maybe] g
continue? [y=yes|n=no|m=maybe] k
continue? [y=yes|n=no|m=maybe] y
$
It will set REPLY
to y
(inside the script).
它将对y(脚本内部)进行回复。
#17
3
I suggest you use dialog...
我建议你使用对话……
Linux Apprentice: Improve Bash Shell Scripts Using Dialog
The dialog command enables the use of window boxes in shell scripts to make their use more interactive.
对话框命令允许在shell脚本中使用窗口框,使它们的使用更具交互性。
it's simple and easy to use, there's also a gnome version called gdialog that takes the exact same parameters, but shows it GUI style on X.
它简单易用,还有一个名为gdialog的gnome版本,它接受相同的参数,但是在X上显示GUI样式。
#18
3
Inspired by the answers of @Mark and @Myrddin I created this function for a universal prompt
受到@Mark和@Myrddin回答的启发,我为通用提示符创建了这个函数
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
}
use it like this:
使用它是这样的:
unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
echo "$selection"
#19
3
One simple way to do this is with xargs -p
or gnu parallel --interactive
.
一种简单的方法是使用xargs -p或gnu并行交互。
I like the behavior of xargs a little better for this because it executes each command immediately after the prompt like other interactive unix commands, rather than collecting the yesses to run at the end. (You can Ctrl-C after you get through the ones you wanted.)
我更喜欢xargs的行为,因为它像其他交互式unix命令一样,在提示符之后立即执行每个命令,而不是在最后收集要运行的yess。(您可以按Ctrl-C完成所需的操作。)
e.g.,
例如,
echo *.xml | xargs -p -n 1 -J {} mv {} backup/
#20
3
more generic would be:
更一般的是:
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
}
#21
2
As a friend of a one line command I used the following:
作为一个单行命令的朋友,我使用了以下命令:
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=;
Written longform, it works like this:
写的很长,是这样的:
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=;
#22
2
I've used the case
statement a couple of times in such a scenario, using the case statment is a good way to go about it. A while
loop, that ecapsulates the case
block, that utilizes a boolean condition can be implemented in order to hold even more control of the program, and fulfill many other requirements. After the all the conditions have been met, a break
can be used which will pass control back to the main part of the program. Also, to meet other conditions, of course conditional statements can be added to accompany the control structures: case
statement and possible while
loop.
在这样的场景中,我多次使用case语句,使用case statment是一种很好的方式。一个while循环,即ecapsulates这个case块,它利用一个布尔条件来实现对程序的更多控制,并满足许多其他的需求。在所有条件都满足之后,可以使用一个中断,它将把控制传回程序的主要部分。此外,为了满足其他条件,当然可以在控制结构中添加条件语句:case语句和可能的while循环。
Example of using a case
statement to fulfill your request
示例使用case语句来满足您的请求。
#! /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
#23
2
I noticed that no one posted an answer showing multi-line echo menu for such simple user input so here is my go at it:
我注意到没有人贴出这样简单的用户输入的多行回波菜单的答案,所以我这样做:
#!/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
This method was posted in the hopes that someone may find it useful and time-saving.
这个方法被发布是希望有人能发现它有用和节省时间。
#24
1
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!'
#25
0
Yes / No / Cancel
Function
#!/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
}
Usage
case $(@confirm 'Confirm?') in
Y ) echo "Yes" ;;
N ) echo "No" ;;
C ) echo "Cancel" ;;
esac
Confirm with clean user input
Function
#!/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
}
Usage
if @confirm 'Confirm?' ; then
echo "Yes"
else
echo "No"
fi
#26
0
In response to others:
在回答别人:
You don't need to specify case in BASH4 just use the ',,' to make a var lowercase. Also I strongly dislike putting code inside of the read block, get the result and deal with it outside of the read block IMO. Also include a 'q' for quit IMO. Lastly why type 'yes' just use -n1 and have the press y.
您不需要在BASH4中指定大小写,只需使用“,”将var设置为小写。我也非常不喜欢把代码放在读块中,得到结果并在读块之外处理它。还包括一个“退出国际海事组织”的“q”。最后,为什么输入“yes”只需要使用-n1,然后按y键。
Example: user can press y/n and also q to just quit.
示例:用户可以按y/n和q来退出。
ans=''
while true; do
read -p "So is MikeQ the greatest or what (y/n/q) ?" -n1 ans
case ${ans,,} in
y|n|q) break;;
*) echo "Answer y for yes / n for no or q for quit.";;
esac
done
echo -e "\nAnswer = $ans"
if [[ "${ans,,}" == "q" ]] ; then
echo "OK Quitting, we will assume that he is"
exit 0
fi
if [[ "${ans,,}" == "y" ]] ; then
echo "MikeQ is the greatest!!"
else
echo "No? MikeQ is not the greatest?"
fi
#1
1249
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:
在shell提示符下获取用户输入的最简单、最广泛的方法是read命令。说明其使用的最佳方法是简单的演示:
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
:
Steven Huwig指出的另一个方法是Bash的select命令。下面是使用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.
使用select,您不需要对输入进行消毒——它将显示可用的选项,然后您键入一个与您的选择相对应的数字。它也会自动循环,所以如果它们给出无效的输入,就不需要一个while循环来重试。
Also, please check out the excellent answer by F. Hauri.
另外,请查看F. Hauri的精彩答案。
#2
363
At least five answers for one generic question.
Depending on
根据
- posix compliant: could work on poor systems with generic shell environments
- posix兼容:可以在具有通用shell环境的糟糕系统上工作
- bash specific: using so called bashisms
- bash专用:使用所谓的bashisms。
and if you want
如果你想
- simple ``in line'' question / answer (generic solutions)
- 简单的“in line”问题/答案(通用解决方案)
- pretty formatted interfaces, like ncurses or more graphical using libgtk or libqt...
- 漂亮的格式化界面,比如ncurses或者使用libgtk或libqt的图形化界面……
- use powerful readline history capability
- 使用强大的读线历史功能
1. POSIX generic solutions
You could use the read
command, followed by if ... then ... else
:
您可以使用read命令,然后是if…然后……其他:
echo -n "Is this a good question (y/n)? "
read answer
# if echo "$answer" | grep -iq "^y" ;then
# (Thanks to Adam Katz's comment: This test is more portable and avoid one fork:)
#(感谢Adam Katz的评论:这个测试更便携,并且避免使用一个fork:)
if [ "$answer" != "${answer#[Yy]}" ] ;then
echo Yes
else
echo No
fi
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.)
(编辑:正如@JonathanLeffler正确地指出的那样,保存stty的配置可能比简单地强迫他们保持清醒要好。)
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
Note: This was tested under sh, bash, ksh, dash and busybox!
注意:这是在sh, bash, ksh, dash和busybox下测试的!
Same, but waiting explicitly for y or n:
同样,但明确地等待y或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
Using dedicated tools
There are many tools which were built using libncurses
, libgtk
, libqt
or other graphical libraries. For example, using whiptail
:
有许多使用libncurses、libgtk、libqt或其他图形库构建的工具。例如,使用鞭尾:
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:
根据您的系统,您可能需要用另一个类似工具替换whiptail:
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.
其中20为对话框的高度(行数),60为对话框的宽度。这些工具都具有几乎相同的语法。
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 ${answer:0:1} 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...
我更喜欢用例,这样我甚至可以测试yes | ja |如果需要的话,|是的…
in line with single key feature
Under bash, we can specify the length of intended input for for the read
command:
在bash中,我们可以为read命令指定预期输入的长度:
read -n 1 -p "Is this a good question (y/n)? " answer
Under bash, read
command accepts a timeout parameter, which could be useful.
在bash中,read命令接受一个超时参数,这可能很有用。
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
Some tricks for dedicated tools
More sophisticated dialog boxes, beyond simple yes - no
purposes:
更复杂的对话框,除了简单的yes—no purpose:
dialog --menu "Is this a good question" 20 60 12 y Yes n No m Maybe
Progress bar:
进度条:
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
)
Little 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
More sample? Have a look at Using whiptail for choosing USB device and USB removable storage selector: USBKeyChooser
更多的样本吗?看看使用whiptail选择USB设备和USB移动存储选择器:USBKeyChooser
5. Using readline's history
Example:
例子:
#!/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
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.
这将创建一个文件。myscript。在$HOME目录中的历史,比使用readline的历史命令要多,比如向上、向下、Ctrl+r等。
#3
332
echo "Please enter some input: "
read input_variable
echo "You entered: $input_variable"
#4
115
You can use the built-in read command ; Use the -p
option to prompt the user with a question.
可以使用内置的read命令;使用-p选项提示用户提出问题。
Since BASH4, you can now use -i
to suggest an answer, so the user only have to press return
to enter it :
由于BASH4现在可以使用-i来建议答案,因此用户只需按return即可进入:
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH
(But remember to use the "readline" option -e
to allow line editing with arrow keys)
(但请记住使用“readline”选项-e允许使用箭头键进行行编辑)
#5
92
Bash has select for this purpose.
Bash已经为此选择了。
select result in Yes No Cancel
do
echo $result
done
#6
51
read -p "Are you alright? (y/n) " RESP
if [ "$RESP" = "y" ]; then
echo "Glad to hear it"
else
echo "You need more bash programming"
fi
#7
27
Here's something I put together:
这是我整理的东西:
#!/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
I'm a beginner, so take this with a grain of salt, but it seems to work.
我是一个初学者,所以对此持怀疑态度,但这似乎行得通。
#8
22
You want:
- Bash builtin commands (i.e. portable)
- Bash内置命令(即可移植)
- Check TTY
- 检查遥控
- Default answer
- 默认的答案
- Timeout
- 超时
- Colored question
- 颜色的问题
Snippet
do_xxxx=y # In batch mode => Default is Yes
[[ -t 0 ]] && # If TTY => Prompt the question
read -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx # Store the answer in $do_xxxx
if [[ $do_xxxx =~ ^(y|Y|)$ ]] # Do if 'y' or 'Y' or empty
then
xxxx
fi
Explanations
-
[[ -t 0 ]] && read ...
=> Call commandread
if TTY - [- t0] && & read…=>调用命令读取if TTY
-
read -n 1
=> Wait for one character - 读取- n1 =>等待一个字符
-
$'\e[1;32m ... \e[0m '
=> Print in green
(green is fine because readable on both white/black backgrounds) - “\ e[1;32美元……\e[0m ' =>绿色打印(绿色可以,因为白色/黑色背景都可以阅读)
-
[[ $do_xxxx =~ ^(y|Y|)$ ]]
=> bash regex - [[$ do_xxxx = ~ ^(y y | |)美元]]= > bash正则表达式
Timeout => Default answer is No
do_xxxx=y
[[ -t 0 ]] && { # Timeout 5 seconds (read -t 5)
read -t 5 -n 1 -p $'\e[1;32m
Do xxxx? (Y/n)\e[0m ' do_xxxx || # read 'fails' on timeout
do_xxxx=n ; } # Timeout => answer No
if [[ $do_xxxx =~ ^(y|Y|)$ ]]
then
xxxx
fi
#9
20
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?"
...
#10
19
The easiest way to achieve this with the least number of lines is as follows:
用最少的行数来实现这个最简单的方法是:
read -p "<Your Friendly Message here> : y/n/cancel" CONDITION;
if [ "$CONDITION" == "y" ]; then
# do something here!
fi
The if
is just an example: it is up to you how to handle this variable.
if只是一个例子:如何处理这个变量取决于您。
#11
15
This solution reads a single character and calls a function on a yes response.
该解决方案读取单个字符并在yes响应上调用函数。
read -p "Are you sure? (y/n) " -n 1
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
do_something
fi
#12
15
Use the read
command:
使用读取命令:
echo Would you like to install? "(Y or N)"
read x
# now check if $x is "y"
if [ "$x" = "y" ]; then
# do something here!
fi
and then all of the other stuff you need
然后你需要的其他东西。
#13
12
read -e -p "Enter your choice: " choice
The -e
option enables the user to edit the input using arrow keys.
e选项允许用户使用箭头键编辑输入。
If you want to use a suggestion as input:
如果你想用一个建议作为输入:
read -e -i "yes" -p "Enter your choice: " choice
-i
option prints a suggestive input.
-i选项打印提示输入。
#14
7
Sorry for posting on such an old post. Some weeks ago I was facing a similar problem, in my case I needed a solution which also worked within an online installer-script, eg: curl -Ss https://raw.github.com/_____/installer.sh | bash
很抱歉在这么旧的帖子上发帖。几个星期前,我遇到了一个类似的问题,在我的例子中,我需要一个同样适用于在线安装脚本的解决方案,例如:curl -Ss https://raw.github.com/_____/installer.sh | bash
Using read yesno < /dev/tty
works fine for me:
使用read yesno < /dev/tty对我来说合适:
echo -n "These files will be uploaded. Is this ok? (y/n) "
read yesno < /dev/tty
if [ "x$yesno" = "xy" ];then
# Yes
else
# No
fi
Hope this helps someone.
希望这可以帮助别人。
#15
5
To get a nice ncurses-like inputbox use the command dialog like this:
要获得一个类似ncurses的输入框,请使用如下所示的命令对话框:
#!/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
The dialog package is installed by default at least with SUSE Linux.
至少在SUSE Linux中,对话框包是默认安装的。
#16
3
Multiple choice version:
多项选择版本:
ask () { # $1=question $2=options
# set REPLY
# options: x=..|y=..
while $(true); do
printf '%s [%s] ' "$1" "$2"
stty cbreak
REPLY=$(dd if=/dev/tty bs=1 count=1 2> /dev/null)
stty -cbreak
test "$REPLY" != "$(printf '\n')" && printf '\n'
(
IFS='|'
for o in $2; do
if [ "$REPLY" = "${o%%=*}" ]; then
printf '\n'
break
fi
done
) | grep ^ > /dev/null && return
done
}
Example:
例子:
$ ask 'continue?' 'y=yes|n=no|m=maybe'
continue? [y=yes|n=no|m=maybe] g
continue? [y=yes|n=no|m=maybe] k
continue? [y=yes|n=no|m=maybe] y
$
It will set REPLY
to y
(inside the script).
它将对y(脚本内部)进行回复。
#17
3
I suggest you use dialog...
我建议你使用对话……
Linux Apprentice: Improve Bash Shell Scripts Using Dialog
The dialog command enables the use of window boxes in shell scripts to make their use more interactive.
对话框命令允许在shell脚本中使用窗口框,使它们的使用更具交互性。
it's simple and easy to use, there's also a gnome version called gdialog that takes the exact same parameters, but shows it GUI style on X.
它简单易用,还有一个名为gdialog的gnome版本,它接受相同的参数,但是在X上显示GUI样式。
#18
3
Inspired by the answers of @Mark and @Myrddin I created this function for a universal prompt
受到@Mark和@Myrddin回答的启发,我为通用提示符创建了这个函数
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
}
use it like this:
使用它是这样的:
unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
echo "$selection"
#19
3
One simple way to do this is with xargs -p
or gnu parallel --interactive
.
一种简单的方法是使用xargs -p或gnu并行交互。
I like the behavior of xargs a little better for this because it executes each command immediately after the prompt like other interactive unix commands, rather than collecting the yesses to run at the end. (You can Ctrl-C after you get through the ones you wanted.)
我更喜欢xargs的行为,因为它像其他交互式unix命令一样,在提示符之后立即执行每个命令,而不是在最后收集要运行的yess。(您可以按Ctrl-C完成所需的操作。)
e.g.,
例如,
echo *.xml | xargs -p -n 1 -J {} mv {} backup/
#20
3
more generic would be:
更一般的是:
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
}
#21
2
As a friend of a one line command I used the following:
作为一个单行命令的朋友,我使用了以下命令:
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=;
Written longform, it works like this:
写的很长,是这样的:
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=;
#22
2
I've used the case
statement a couple of times in such a scenario, using the case statment is a good way to go about it. A while
loop, that ecapsulates the case
block, that utilizes a boolean condition can be implemented in order to hold even more control of the program, and fulfill many other requirements. After the all the conditions have been met, a break
can be used which will pass control back to the main part of the program. Also, to meet other conditions, of course conditional statements can be added to accompany the control structures: case
statement and possible while
loop.
在这样的场景中,我多次使用case语句,使用case statment是一种很好的方式。一个while循环,即ecapsulates这个case块,它利用一个布尔条件来实现对程序的更多控制,并满足许多其他的需求。在所有条件都满足之后,可以使用一个中断,它将把控制传回程序的主要部分。此外,为了满足其他条件,当然可以在控制结构中添加条件语句:case语句和可能的while循环。
Example of using a case
statement to fulfill your request
示例使用case语句来满足您的请求。
#! /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
#23
2
I noticed that no one posted an answer showing multi-line echo menu for such simple user input so here is my go at it:
我注意到没有人贴出这样简单的用户输入的多行回波菜单的答案,所以我这样做:
#!/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
This method was posted in the hopes that someone may find it useful and time-saving.
这个方法被发布是希望有人能发现它有用和节省时间。
#24
1
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!'
#25
0
Yes / No / Cancel
Function
#!/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
}
Usage
case $(@confirm 'Confirm?') in
Y ) echo "Yes" ;;
N ) echo "No" ;;
C ) echo "Cancel" ;;
esac
Confirm with clean user input
Function
#!/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
}
Usage
if @confirm 'Confirm?' ; then
echo "Yes"
else
echo "No"
fi
#26
0
In response to others:
在回答别人:
You don't need to specify case in BASH4 just use the ',,' to make a var lowercase. Also I strongly dislike putting code inside of the read block, get the result and deal with it outside of the read block IMO. Also include a 'q' for quit IMO. Lastly why type 'yes' just use -n1 and have the press y.
您不需要在BASH4中指定大小写,只需使用“,”将var设置为小写。我也非常不喜欢把代码放在读块中,得到结果并在读块之外处理它。还包括一个“退出国际海事组织”的“q”。最后,为什么输入“yes”只需要使用-n1,然后按y键。
Example: user can press y/n and also q to just quit.
示例:用户可以按y/n和q来退出。
ans=''
while true; do
read -p "So is MikeQ the greatest or what (y/n/q) ?" -n1 ans
case ${ans,,} in
y|n|q) break;;
*) echo "Answer y for yes / n for no or q for quit.";;
esac
done
echo -e "\nAnswer = $ans"
if [[ "${ans,,}" == "q" ]] ; then
echo "OK Quitting, we will assume that he is"
exit 0
fi
if [[ "${ans,,}" == "y" ]] ; then
echo "MikeQ is the greatest!!"
else
echo "No? MikeQ is not the greatest?"
fi