我想在shell脚本中暂停输入,并提示用户进行选择.标准的"是,否或取消"类型问题.如何在典型的bash提示符中完成此操作?
在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
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
随着select
你并不需要净化输入-它显示可用的选项,你键入相应的你的选择一个号码.它也会自动循环,因此while true
如果它们提供无效输入,则无需重试循环.
另外,请查看F. Hauri 的优秀答案.
取决于
符合posix:可以在具有通用shell环境的不良系统上工作
bash具体:使用所谓的bashisms
如果你想要的话
简单的``在线''问题/答案(通用解决方案)
相当格式化的接口,如使用libgtk或libqt的ncurses或更多图形...
使用强大的readline历史记录功能
您可以使用该read
命令,然后执行以下操作if ... then ... else
:
echo -n "Is this a good question (y/n)? " read answer
# if echo "$answer" | grep -iq "^y" ;then
if [ "$answer" != "${answer#[Yy]}" ] ;then echo Yes else echo No fi
(感谢Adam Katz的评论:将上面的测试替换为一个更便携的测试并避免使用一个分叉:)
但如果您不希望用户必须点击Return,您可以写:
(编辑:正如@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
注意:这是在sh,bash,ksh,dash和busybox下测试的!
相同,但明确等待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
有很多其使用内置的工具libncurses
,libgtk
,libqt
或其他图形库.例如,使用whiptail
:
if whiptail --yesno "Is this a good question" 20 60 ;then echo Yes else echo No fi
根据您的系统,您可能需要更换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
20
对话框的高度在哪里,行数60
是对话框的宽度.这些工具都具有几乎相同的语法.
DIALOG=whiptail if [ -x /usr/bin/gdialog ] ;then DIALOG=gdialog ; fi if [ -x /usr/bin/xdialog ] ;then DIALOG=xdialog ; fi ... $DIALOG --yesno ...
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
除了简单的yes - no
目的之外,更复杂的对话框:
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" ) || 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
更多样品?看看使用whiptail选择USB设备和USB可移动存储选择器:USBKeyChooser
例:
#!/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
这将创建一个文件.myscript.history
在$HOME
目录中,比你能使用输入行的历史命令,比如Up, Down,Ctrl+ r等.
echo "Please enter some input: " read input_variable echo "You entered: $input_variable"
您可以使用内置的read命令; 使用该-p
选项可向用户提示问题.
从BASH4开始,您现在可以使用-i
建议答案,因此用户只需按下-e
即可输入:
read -e -p "Enter the path to the file: " -i "/usr/local/etc/" FILEPATH echo $FILEPATH
(但请记住使用"readline"选项-p
允许使用箭头键进行行编辑)
如果你想要一个"是/否"逻辑,你可以这样做:
read -e -p " List the content of your home dir ? [Y/n] " YN [[ $YN == "y" || $YN == "Y" || $YN == "" ]] && ls -la ~/
Bash已经选择了这个目的.
select result in Yes No Cancel do echo $result done
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
这是我放在一起的东西:
#!/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
我是一个初学者,所以带上一粒盐,但似乎有效.
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?" ...
Bash内置命令(即便携式)
检查TTY
默认答案
超时
有色的问题
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
[[ -t 0 ]] && read ...
=> read
如果TTY,则调用命令
read -n 1
=>等一个字符
$'\e[1;32m ... \e[0m '
=>以绿色打印
(绿色很好,因为白色/黑色背景都可读)
[[ $do_xxxx =~ ^(y|Y|)$ ]]
=> bash正则表达式
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
使用最少行数实现此目的的最简单方法如下:
read -p ": y/n/cancel" CONDITION; if [ "$CONDITION" == "y" ]; then # do something here! fi
这if
只是一个例子:由你决定如何处理这个变量.
使用read
命令:
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
然后你需要的所有其他东西
此解决方案读取单个字符并在yes响应上调用函数.
read -p "Are you sure? (y/n) " -n 1 echo if [[ $REPLY =~ ^[Yy]$ ]]; then do_something fi
read -e -p "Enter your choice: " choice
该-e
选项使用户可以使用箭头键编辑输入.
如果您想使用建议作为输入:
read -e -i "yes" -p "Enter your choice: " choice
-i
选项打印暗示输入.
您可以在REPLY
上使用默认值read
,将其转换为小写并与带有表达式的一组变量进行比较。
该脚本还支持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
很抱歉在这么老的帖子上发帖.几周前我遇到了类似的问题,在我的情况下,我需要一个解决方案,它也可以在一个在线安装程序脚本中工作,例如:curl -Ss https://raw.github.com/_____/installer.sh | bash
使用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
希望这有助于某人.
要获得一个很好的类似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中.
这是一个更长,但可重复使用和模块化的方法:
返回0
=是和1
=否
无需按下输入 - 只需一个字符
可以按下enter接受默认选择
可以禁用默认选项以强制选择
适用于zsh
和bash
.
请注意,这N
是资本.按Enter键,接受默认值:
$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]?
另请注意,这[y/N]?
是自动附加的.接受默认的"no",因此不会回显任何内容.
重新提示,直到给出有效的回复:
$ confirm "Show dangerous command" && echo "rm *"
Show dangerous command [y/N]? X
Show dangerous command [y/N]? y
rm *
请注意,Y
大写:
$ confirm_yes "Show dangerous command" && echo "rm *"
Show dangerous command [Y/n]?
rm *
上面,我只是按了回车键,所以命令运行了.
y
或n
$ get_yes_keypress "Here you cannot press enter. Do you like this [y/n]? "
Here you cannot press enter. Do you like this [y/n]? k
Here you cannot press enter. Do you like this [y/n]?
Here you cannot press enter. Do you like this [y/n]? n
$ echo $?
1
在这里,1
或者返回false.请注意,使用此较低级别的功能,您需要提供自己的[y/n]?
提示.
# Read a single char from /dev/tty, prompting with "$*"
# Note: pressing enter will return a null string. Perhaps a version terminated with X and then remove it in caller?
# See https://unix.stackexchange.com/a/367880/143394 for dealing with multi-byte, etc.
function get_keypress {
local REPLY IFS=
>/dev/tty printf '%s' "$*"
[[ $ZSH_VERSION ]] && read -rk1 # Use -u0 to read from STDIN
# See https://unix.stackexchange.com/q/383197/143394 regarding '\n' -> ''
[[ $BASH_VERSION ]] &&
# Usage: confirm "Dangerous. Are you sure?" && rm *
function confirm {
local prompt="${*:-Are you sure} [y/N]? "
get_yes_keypress "$prompt" 1
}
# Prompt to confirm, defaulting to YES on
function confirm_yes {
local prompt="${*:-Are you sure} [Y/n]? "
get_yes_keypress "$prompt" 0
}
我注意到没有人发布这样一个简单的用户输入显示多行回显菜单的答案,因此我可以这样做:
#!/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
发布此方法是为了希望有人发现它有用且节省时间。