我想暂停shell脚本中的输入,并提示用户进行选择。标准的是、否或取消类型问题。如何在典型的bash提示符中完成此操作?
当前回答
您可以使用内置的read命令;使用-p选项向用户提示问题。
从BASH4开始,您现在可以使用-i建议答案:
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH
echo $FILEPATH
(但请记住使用“readline”选项-e以允许使用箭头键进行行编辑)
如果你想要一个“是/否”的逻辑,你可以这样做:
read -e -p "
List the content of your home dir ? [Y/n] " YN
[[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
其他回答
您可以在读取时使用默认REPLY,将其转换为小写,并使用表达式与一组变量进行比较。该脚本还支持ja/si/oui
read -rp "Do you want a demo? [y/n/c] "
[[ ${REPLY,,} =~ ^(c|cancel)$ ]] && { echo "Selected Cancel"; exit 1; }
if [[ ${REPLY,,} =~ ^(y|yes|j|ja|s|si|o|oui)$ ]]; then
echo "Positive"
fi
要获得像inputbox这样的漂亮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
默认情况下,对话框包至少在SUSE Linux上安装。看起来像:
受到@Mark和@Myrdin的回答的启发,我创建了一个通用提示的函数
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
}
这样使用:
unipromtp "Select an option: (a)-Do one (x)->Do two (f)->Do three : " "a x f" selection
echo "$selection"
一个通用问题至少有五个答案。
取决于
posix兼容:可以在具有通用shell环境的较差系统上工作特定于bash:使用所谓的bashms
如果你想
简单的“在线”问题/答案(通用解决方案)漂亮的格式化界面,如ncurses或更多使用libgtk或libqt的图形界面。。。使用强大的读取线历史记录功能
1.POSIX通用解决方案
您可以使用read命令,后跟if。。。然后其他:
printf 'Is this a good question (y/n)? '
read answer
#如果echo“$answer”| grep-iq“^y”;然后
if [ "$answer" != "${answer#[Yy]}" ] ;then # this grammar (the #[] operator) means that the variable $answer where any Y or y in 1st position will be dropped if they exist.
echo Yes
else
echo No
fi
(感谢Adam Katz的评论:将上面的测试替换为更便携且避免使用分叉的测试:)
POSIX,但一键功能
但如果你不想让用户点击Return,你可以这样写:
(编辑:正如@JonathanLeffler正确地建议的那样,保存stty的配置可能比简单地强迫他们保持理智要好。)
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
注意:这是在sh、bash、ksh、dash和busybox下测试的!
相同,但明确等待y或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
使用专用工具
有许多工具是使用libncurses、libgtk、libqt或其他图形库构建的。例如,使用whibtail:
if whiptail --yesno "Is this a good question" 20 60 ;then
echo Yes
else
echo No
fi
根据您的系统,您可能需要用另一个类似的工具替换whibtail:
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
其中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.具体的解决方案
基本在线方法
read -p "Is this a good question (y/n)? " answer
case ${answer:0:1} in
y|Y )
echo Yes
;;
* )
echo No
;;
esac
我更喜欢使用case,所以如果需要,我甚至可以测试yes | ja | si | oui。。。
符合单键功能
在bash下,我们可以为read命令指定预期输入的长度:
read -n 1 -p "Is this a good question (y/n)? " answer
在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
带倒计时的超时:
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.专用工具的一些技巧
除了简单的“是-否”目的之外,更复杂的对话框:
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=$($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
更多样品?查看使用whiptail选择USB设备和USB可移动存储选择器:USBKeyChooser
5.使用readline的历史记录
例子:
#!/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
这将在$HOME目录中创建一个文件.myscript.history,您可以使用readline的历史记录命令,如Up、Down、Ctrl+r等。
多选版本:
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
}
例子:
$ 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
$
它会将REPLY设置为y(在脚本中)。