linux - variable - ubuntu shell script
如何在Linux shell腳本中提示是/否/取消輸入? (17)
一個通用問題至少有五個答案。
取決於
- 符合posix :可以在通用shell環境下運行在較差的系統上
- bash具體:使用所謂的bashisms
如果你想
- 簡單``在線''問題/答案(通用解決方案)
- 相當格式化的接口,如ncurses或更多圖形使用libgtk或libqt ...
- 使用強大的readline歷史記錄功能
1. POSIX通用解決方案
你可以使用read
命令,接下來是if ... then ... else
:
echo -n "Is this a good question (y/n)? "
read answer
# if echo "$answer" | grep -iq "^y" ;then
#(感謝Adam Katz的評論 :這個測試更便攜,避免一個分叉:)
if [ "$answer" != "${answer#[Yy]}" ] ;then
echo Yes
else
echo No
fi
POSIX,但單一的關鍵功能
但是如果你不希望用戶必須點擊返回 ,你可以寫:
( 編輯:正如@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
或其他圖形庫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
,對話框的寬度是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
yes | ja | si | oui
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
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等等。
我想在shell腳本中暫停輸入,並提示用戶進行選擇。 標準的“是,否,或取消”類型問題。 我如何在典型的bash提示符下完成此操作?
是/否/取消
功能
#!/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
}
用法
case $(@confirm 'Confirm?') in
Y ) echo "Yes" ;;
N ) echo "No" ;;
C ) echo "Cancel" ;;
esac
用乾淨的用戶輸入確認
功能
#!/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
}
用法
if @confirm 'Confirm?' ; then
echo "Yes"
else
echo "No"
fi
Bash已經為此select了。
select result in Yes No Cancel
do
echo $result
done
一個簡單的方法是使用xargs -p
或gnu parallel --interactive
。
我喜歡xargs的行為,因為它會像其他交互式unix命令一樣,在提示符後立即執行每條命令,而不是收集最後運行的yesses。 (你可以通過Ctrl-C來完成你想要的。)
例如,
echo *.xml | xargs -p -n 1 -J {} mv {} backup/
作為單行命令的朋友,我使用了以下內容:
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=;
書面longform,它的工作原理是這樣的:
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=;
使用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
然後是所有你需要的其他東西
在這種情況下,我已經多次使用case case語句,使用case statment是一個很好的解決方法。 一個while
循環可以實現一個利用布爾條件的case
塊來實現對程序的更多控制,並滿足許多其他需求。 在滿足所有條件之後,可以使用一個break
來將控制權交還給程序的主要部分。 此外,為了滿足其他條件,當然可以添加條件語句以伴隨控制結構: case
語句和可能的while
循環。
使用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
多選版本:
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
(在腳本內)。
我建議你使用對話框 ...
Linux學徒:使用對話框改進Bash Shell腳本
對話框命令使得在shell腳本中使用窗口框可以更加交互地使用它們。
它簡單易用,還有一個名為gdialog的gnome版本,它採用完全相同的參數,但在X上顯示它的GUI風格。
我注意到,沒有人發布了一個答案,顯示了這樣簡單的用戶輸入的多行迴聲菜單,所以這裡是我去的地方:
#!/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
這種方法發布的希望是有人可能覺得它有用並且省時。
更通用的是:
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
}
為了得到一個漂亮的類似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中默認安裝對話框。
這是我放在一起的東西:
#!/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
我是一個初學者,所以拿一點鹽,但它似乎工作。
在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的優秀答案 。
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
-e
選項使用戶能夠使用箭頭鍵編輯輸入。
如果你想使用建議作為輸入:
read -e -i "yes" -p "Enter your choice: " choice
-i
選項打印一個暗示輸入。
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!'