[轉] 利用shell創建文本菜單與窗口部件的方法


[From] http://www.jb51.net/article/129460.htm

 

前言

創建交互式shell腳本最常用的方法是使用菜單。提供各種選項可以幫助腳本用戶了解腳本能做什么,不能做什么;通常菜單腳本會清空顯示區域,然后顯示可用的選項列表。本文給大家詳細介紹了shell創建文本菜單與窗口部件的相關內容,分享出來供大家參考學習,下面話不多說了,來一起看看詳細的介紹吧。

創建文本菜單

創建交互式shell腳本最常用的方法是使用菜單,它提供了各種選項幫助腳本用戶了解腳本能做到的和不能做的。

shell腳本菜單的核心是case命令,該命令會根據用戶在菜單上的選擇來執行特定命令。

下面我們逐步了解和創建基於菜單的shell腳本的步驟。

創建菜單布局

第一步是決定在菜單上顯示哪些元素以及想要顯示的布局方式。

在創建菜單前,通常先清空顯示器上已有的內容。這樣能在干凈的,沒有干擾的環境中顯示菜單了。

clear命令使用當前終端的terminfo數據來清理出現在屏幕上的文字。運行clear命令后可以使用echo命令顯示菜單元素。

默認,echo命令只顯示可打印的文本字符。而在創建菜單時一些非文本字符也非常有用,比如制表符和換行符。我們需要添加-e選項使得echo命令能解析包含在其中的非文本字符。

例如,

?
1
2
wsx@wsx:~/tmp$ echo -e "1.\tDisplay disk space"
1. Display disk space

這對於格式化菜單項布局非常方便,只需要幾個echo命令就可以創建一個還不錯的菜單。

?
1
2
3
4
5
6
7
8
clear
echo
echo -e "\t\t\tSys Admin Menu\n"
echo -e "\t1. Display disk space"
echo -e "\t2. Display logged on users"
echo -e "\t3. Display memory usage"
echo -e "\t0. Exit menu\n\n"
echo -en "\t\tEnter an option: "

最后一行-en選項去掉末尾換行符使得菜單更專業點,光標會在行尾等待用戶輸入。

創建菜單的最后一步是獲取用戶輸入。這一步用read命令。因為我們只期望用戶使用單字符輸入,在命令加-n選項進行限定。這樣用戶只需要輸入一個數字,不用摁回車鍵。

?
1
read -n 1 option

創建菜單函數

shell腳本菜單選項作為一組獨立的函數實現起來更為容易。要做到這一點,你要為每個菜單項創建獨立的shell函數。第一步是決定你希望腳本執行那些功能,然后將這些功能以函數的形式放在代碼中。

通常我們會為還沒有實現的函數先創建一個樁函數,它是一個控函數,或者只有一個echo語句,說明最終這里需要什么內容。

?
1
2
3
4
function diskspace {
  clear
  echo "This is where the diskspace commands will do"
}

這允許菜單在我實現某個函數時仍然能正常操作。不需要我們寫出所有函數之后才能讓菜單投入使用。函數從clear命令開始,這樣我們就能在一個干凈的屏幕上執行該函數,不會收到原先菜單的干擾。

另外,將菜單布局本身作為一個函數來創建有利於菜單制作。

?
1
2
3
4
5
6
7
8
9
10
11
function menu {
  clear
  echo
  echo -e "\t\t\tSys Admin Menu\n"
  echo -e "\t1. Display disk space"
  echo -e "\t2. Display logged on users"
  echo -e "\t3. Display memory usage"
  echo -e "\t0. Exit menu\n\n"
  echo -en "\t\tEnter an option: "
  read -n 1 option
}

這樣我們能在任何時候調用該函數以此重現菜單。

添加菜單邏輯

下一步我們需要創建程序邏輯將菜單布局和函數結合起來。這需要使用case命令。

case命令應該根據菜單中輸入的字符來調用相應的函數,用case命令字符星號來處理所有不正確的菜單項。

下面展示了典型菜單的case用法:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
menu
case $option in
0)
  break ;;
1)
  diskspace ;;
2)
  whoseon ;;
3)
  memusage ;;
*)
  clear
  echo "Sorry, wrong selection" ;;
esac

這里首先調用menu函數清空屏幕並顯示菜單。menu函數中的read命令會一直等待,知道用戶在鍵盤上鍵入一個字符。然后case命令會接管余下的處理過程,基於字符調用相應的函數。

整合shell腳本菜單

現在讓我們將前面的步驟全部組合起來,看看它們是如何協作的。

這是一個完整的菜單腳本例子:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
wsx@wsx:~ /tmp $ cat test14
#!/bin/bash
# simple script menu
 
function diskspace {
  clear
  df -k
}
 
function whoseon {
  clear
  who
}
 
function memusage {
  clear
  cat /proc/meminfo
}
 
function menu {
  clear
  echo
  echo -e "\t\t\tSys Admin Menu\n"
  echo -e "\t1. Display disk space"
  echo -e "\t2. Display logged on users"
  echo -e "\t3. Display memory usage"
  echo -e "\t0. Exit menu\n\n"
  echo -en "\t\tEnter an option: "
  read -n 1 option
}
 
while [ 1 ]
do
  menu
  case $option in
  0)
  break ;;
  1)
  diskspace ;;
  2)
  whoseon ;;
  3)
  memusage ;;
  *)
  clear
  echo "Sorry, wrong selection" ;;
  esac
  echo -en "\n\n\t\t\tHit any key to continue"
  read -n 1 line
done
clear

使用:

?
1
2
3
4
5
6
7
8
9
Sys Admin Menu
 
1. Display disk space
2. Display logged on users
3. Display memory usage
0. Exit menu
 
 
Enter an option:

輸入1:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
文件系統   1K-塊  已用  可用 已用% 掛載點
udev    4006080  0 4006080 0% /dev
tmpfs    807220 81004 726216 11% /run
/dev/sda4  305650672 14226064 275828680 5% /
tmpfs   4036100  1724 4034376 1% /dev/shm
tmpfs    5120  4  5116 1% /run/lock
tmpfs   4036100  0 4036100 0% /sys/fs/cgroup
/dev/sda3   524272  4684 519588 1% /boot/efi
tmpfs    807220  52 807168 1% /run/user/1000
tmpfs    807220  16 807204 1% /run/user/125
/dev/sda2  421886972 23340376 398546596 6% /media/wsx/ 存儲
 
 
  Hit any key to continue

其他都可以自己測試一下,我就不贅言了。

使用select命令

select命令只需要一條命令就可以創建出菜單,然后獲取輸入的答案並自動處理。

命令格式如下:

?
1
2
3
4
select variable in list
do
  commands
done

list參數是由空格分隔的文本選項列表,這些列表構成了整個菜單。select命令會將每個列表項顯示成一個帶編號的選項,然后為選項顯示一個由PS3環境變量定義的特殊提示符。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
wsx@wsx:~ /tmp $ cat smenu1
#!/bin/bash
# using select in the menu
 
function diskspace {
  clear
  df -k
}
 
function whoseon {
  clear
  who
}
 
function memusage {
  clear
  cat /proc/meminfo
}
 
PS3= "Enter an option: "
select option in "Display disk space" "Display logged on users" "Display memory usage" "Exit program"
do
  case $option in
  "Exit program" )
  break ;;
  "Display disk space" )
  diskspace ;;
  "Display logged on users" )
  memusage ;;
  "Display memory usage"
  memusage ;;
  *)
  clear
  echo "Sorry, wrong selection" ;;
  esac
done
clear

運行會自動生成如下菜單項:

?
1
2
3
4
wsx@wsx:~ /tmp $ . /smenu1
1) Display disk space  3) Display memory usage
2) Display logged on users 4) Exit program
Enter an option:

在使用select命令時,記住存儲在變量中的結果值是整個文本字符串而不是跟菜單項相關聯的數字。文本字符串是要在case語句中比較的內容。

制作窗口

dialog包能夠用ANSI轉義控制字符在文本環境中創建標准的窗口對話框。我們可以將這些對話框融入自己的shell腳本中,借此與用戶進行交互。這部分我們來學習如何使用dialog包。

安裝:

?
1
sudo apt-get install dialog

dialog包

dialog包使用命令行參數來決定生成哪種窗口部件(widget)。部件是dialog包中窗口元素的術語。

 

部件 描述
calendar 提供選擇日期的日歷
checklist 顯示多個選項(其中每個選項都能打開或關閉)
form 構建一個帶有標簽以及文本字段(可以填寫內容)的表單
fselect 提供一個文件選擇窗口來瀏覽選擇文件
gauge 顯示完成的百分比進度條
infobox 顯示一條消息,但不用等待回應
inputbox 提供一個輸入文本用的文本表單
inputmenu 提供一個可編輯的菜單
menu 顯示可選擇的一系列選項
msgbox 顯示一條消息,並要求用戶選擇OK按鈕
pause 顯示一個進度條來顯示暫定期間的狀態
passwordbox 顯示一個文本框,但會隱藏輸入的文本
passwordform 顯示一個帶標簽的隱藏文本字段的表單
radiolist 提供一組菜單選項,但只能選擇其中一個
tailbox 用tail命令在滾動窗口中顯示文件的內容
tailboxbg 跟tailbox一樣,但是在后台模式中運行
textbox 在滾動窗口中顯示文件的內容
timebox 提供一個選擇小時、分鍾和秒數的窗口
yesno 提供一條帶有Yes和No按鈕的簡單消息

 

 

 

如上表所見,我們可以選擇很多不同的部件。只需要多花點功夫就可以讓腳本看起來更專業。

要在命令行上指定某個特定的部件,需要使用雙破折線格式:

?
1
dialog --widget parameters

其中widget是上表中某個特定的部件,parameters定義了部件窗口的大小以及部件需要的文本。

每個dialog部件都提供了兩種形式的輸出:

  • 使用STDERR
  • 使用退出狀態碼

可以通過dialog命令的退出狀態碼來確定用戶選擇的按鈕。如果選擇了Yes或OK按鈕,命令會返回狀態碼0。如果選擇了Cancer或No按鈕,命令會返回狀態碼1。可用標准的$?變量來確定dialog部件具體選擇了哪個按鈕。

如果部件返回了數據,dialog命令會將數據發送到STDERR。我們可以用標准的bash shell方法將其重定向到另一個文件或文件描述符中。

?
1
dialog --inputbox "Enter your age:" 10 20 2>age.txt

這條命令將文本框輸入的文本重定向到age.txt文本中。

msgbox部件

msgbox部件是對話框中最常見的類型。它會在窗口中顯示一條簡單的信息,直到用戶點擊OK后消失。

使用格式:

?
1
dialog --msgbox text height width

text參數是要在窗口顯示的字符串,height與width參數設定自動換行的窗口大小。如果想要在窗口加一個標題,可以使用--title參數,后接作為標題的文本。

例子:

?
1
dialog --title Testing --msgbox "This is a test" 10 20

輸入命令后,消息框會顯示在終端上。如果你的終端仿真器支持鼠標,可以單擊OK來關閉對話框,也可以按下回車鍵。

yesno部件

該部件在窗口底部生成兩個按鈕:一個是Yes,一個是No。用戶可以用鼠標、制表符或者鍵盤方向鍵來切換按鈕。選擇按鈕則使用空格或者回車鍵。

下面是一個例子:

?
1
2
3
4
wsx@wsx:~$ dialog --title "Please answer" --yesno "Is this thing on?" 10 20
# 中間終端有輸出
wsx@wsx:~$ echo $?
0

dialog命令的退出狀態碼會根據用戶選擇的按鈕來設置。選擇No返回1,選擇Yes就是0。

inputbox部件

inputbox部件提供了一個簡單的文本框區域來輸入文本字符串,dialog會將它傳到STDERR,我們需要重定向獲得輸入。inputbox提供了兩個按鈕:OK和Cancel。如果選擇了OK,命令退出狀態碼為0,否則為1。

?
1
2
3
wsx@wsx:~ /tmp $ dialog --inputbox "Enter your age:" 10 20 2>age.txt
wsx@wsx:~ /tmp $ cat age.txt
24

如果你自己運行過的話就會注意到該值后面沒有換行符,這讓我們能夠輕松將文本內容重定向到腳本變量中,以獲得用戶輸入的值。

textbox部件

textbox部件是在窗口中顯示大量信息的極佳辦法。它會生成一個滾動窗口來顯示由參數所指定的文件中的文本。

?
1
wsx@wsx:~ /tmp $ dialog --textbox /etc/passwd 15 45

/etc/passwd文件內容顯示在可滾動的文本窗口中,可以用方向鍵來左右或上下滾動顯示文件的內容。窗口底部的行會顯示當前查看文本處於文件中的哪個位置(百分比)。文本框只包含一個用來選擇退出部件的Exit按鈕。

menu部件

我們可以用這個部件來創建之前(上一篇筆記)中制作的文本菜單的窗口版本。只要為每個選項提供一個選擇標號和文本就行。

?
1
wsx@wsx:~ /tmp $ dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 4 "Exit" 2> test .txt

第一個參數定義了菜單的標題,之后的兩個參數定義了菜單窗口的高和寬,而第四個參數則定義了在窗口中一次顯示的菜單項總數。如果存在更多選擇,則有滾動條。

菜單項必須使用菜單對:第一個元素是用來選擇菜單項的標號(必須唯一);第二個元素是菜單中使用的文本。

dialog命令會將選定(鼠標點擊或回車鍵或選擇OK)的菜單項文本發送到STDERR。

fselect部件

該部件在處理文件名時非常方便。不用強制用戶鍵入文件名,我們就可以用fselect部件來瀏覽文件的位置並選擇文件。

使用格式:

?
1
wsx@wsx:~ /tmp $ dialog --title "Select a file" --fselect $HOME/ 10 50 2> file .txt

第一個參數是窗口使用的其實目錄位置。fselect部件窗口由左側的目錄列表、右側的文件列表和含有當前選定的文件或目錄的簡單文本框組成。可以手動在文本框鍵入文件名,或者用目錄和文件列表來選定(使用空格鍵選定)。

dialog選項

除了標准部件,dialog還有大量定制的選項。前面我們使用的title就是一個。

下面顯示了命令可用的選項:

 

選項 描述
–add-widget 繼續下一個對話框直到按下Esc或Cancel選項
–aspect ratio 直到窗口寬度和高度的寬高比
–backtitle title 直到顯示在屏幕頂部背景上的圖標
–begin x y 指定窗口左上角的起始位置
–cancel-label label 指定Cancel按鈕的替代標簽
–clear 用默認的對話背景色來清空屏幕內容
–colors 在對話文本中嵌入ANSI色彩編碼
–cr-wrap 在對話文本中允許使用換行符並強制換行
–create-rc file 將示例配置文件的內容復制到指定的file文件中
–defaultno 將yes/no對話框的默認答案設為no
–default-item string 設定復選列表、表單或菜單對話中的默認項
–exit-label label 指定Exit按鈕的替代標簽
–extra-button 在OK按鈕和Cancel按鈕之間顯示一個額外按鈕
–extra-label label 指定額外按鈕的替換標簽
–help 顯示dialog命令的幫助信息
–help-button 在OK按鈕和Cancel按鈕后顯示一個Help按鈕
–help-label label 指定Help按鈕的替換標簽
–help-status 當選定Help按鈕后,在幫助信息后寫入多選列表、單選列表或表單信息
–ignore 忽略dialog不能識別的選項
–input-fd fd 指定STDIN之外的另一個文件描述符
–insecure 在passwd部件中鍵入內容時顯示星號
–item-help 為多選列表、單選列表或菜單中的每個標號在屏幕底部添加一個幫助欄
–keep-window 不要清除屏幕上顯示過的部件
–max-input size 指定輸入的最大字符串長度。默認為2048
–nocancel 隱藏Cancel按鈕
–no-collapse 不要將對話文本中的制表符轉換為空格
–no-kill 將tailboxbg對話放到后台,並禁止該進程的SIGHUP信號
–no-label label 為No按鈕指定替換標簽
–no-shadow 不要顯示對話窗口的陰影效果
–ok-label label 指定OK按鈕的替換標簽
–output-fd fd 指定除STDERR之外的另一個輸出文件描述符
–print-maxsize 將對話窗口的最大尺寸打印到輸出中
–print-size 將每個對話窗口的大小打印到輸出中
–print-version 將dialog的版本號打印到輸出中
–separate-output 一次一行地輸出checklist部件的結果,不使用引號
–separator string 指定用於分隔部件輸出的字符串
–separate-widget string 指定用於分隔部件輸出的字符串
–shadow 在每個窗口右下角繪制陰影
–single-quoted 需要時對多選列表的輸出采用單引號
–sleep sec 在處理完對話窗口后延遲指定的秒數
–stderr 將輸出發送到STDERR(默認)
–stdout 將輸出發送到STDOUT
–tab-correct 將制表符轉換為空格
–tab-len n 指定一個制表符占用的空格數(默認為8)
–timeout sec 指定無用戶輸入時,sec秒后退出並返回錯誤代碼
–title title 指定對話窗口的標題
–trim 從對話文本中刪除前導空格和換行符
–visit-tiems 修改對話窗口制表符的停留位置,使其包括選項列表
–yes-label label 為Yes按鈕指定替換標簽

 

 

 

--backtitle選項是為腳本中的菜單創建公共標題的簡便辦法。上表提供的強大特性允許我們創建任何需要的窗口。

dialog命令支持運行時配置。該命令會根據配置文件模板創建一份配置文件。dialog啟動時會先去檢查是否設置了DIALOGRC環境變量,該變量會保存配置文件名信息。如果未設置該變量或未找到該文件,它會將$HOME/.dialogrc作為配置文件。如果這個文件還不存在的話就嘗試查找編譯時指定的GLOBALRC文件,也就是/etc/dialogrc。如果還不存在就用編譯時的默認值。

在腳本中使用dialog命令

必須記住兩件事:

  • 如果有Cancel或No按鈕,檢查dialog命令的退出狀態碼
  • 重定向STDERR來獲得輸出值

接下來是一個簡單的實例,使用dialog部件生成我們之前(上一篇筆記)所創建的系統管理菜單。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
wsx@wsx-laptop:~$ cat menu3
#!/bin/bash
# using dialog to create a menu
 
temp=$(mktemp -t test .XXXXXX)
temp2=$(mktemp -t test2.XXXXXX)
 
function diskspace {
   df -k > $temp
   dialog --textbox $temp 20 60
}
 
function whoseon {
   who > $temp
   dialog --textbox $temp 20 50
}
 
function menusage {
   cat /proc/meminfo > $temp
   dialog --textbox $temp 20 50
}
 
while [ 1 ]
do
dialog --menu "Sys Admin Menu" 20 30 10 1 "Display disk space" 2 "Display users" 3 "Display memory usage" 0 "Exit" 2> $temp2
if [ $? - eq 1 ]
then
  break
fi
 
selection=$( cat $temp2)
 
case $selection in
1)
   diskspace ;;
2)
   whoseon ;;
3)
   memusage ;;
0)
   break ;;
*)
   dialog --msgbox "Sorry, invalid selection" 10 30
esac
done
rm -f $temp 2> /dev/null
rm -f $temp 2> /dev/null

使用while循環加一個真值常量創建了一個無限循環來顯示菜單對話。當執行完每個函數后,腳本會返回繼續顯示菜單。

腳本使用了mktemp命令創建兩個臨時文件來保存dialog命令的數據。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對腳本之家的支持。


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2020 ITdaan.com