最近更新: 2011-03-11

在 shell script 中使用圖形式互動元件 - dialog 與 zenity 使用範例

在 Unix 世界中,系統管理者普遍會用 shell script 撰寫簡單的管理工具。 為了強化易用性,簡化 shell script 的撰寫工作,就出現了專門負責提供圖形式互動元件的小工具, 可供我們在 shell script 中調用。 讓它們處理複雜的使用者輸入工作,同時也提高了 shell script 的使用親和力。

本文提供其中兩種工具的使用範例。

  • 一、文字終端機環境: dialog。 非 Ubuntu 預裝項目。
  • 二、GNOME桌面環境: zenity。 這是 GNOME Desktop 計劃下的一個小工具。為 Ubuntu 桌面版本預裝項目,Ubuntu 桌面版都會安裝此套件。

判斷使用環境

dialog 與 zenity 所提供的使用元件基本上是一致的。 若你想要寫一個具有圖形式輸入元件的 shell script ,且當其執行於桌面環境時 zenity ,執行於純文字終端環境時則用 dialog。那麼可以參考下列的判斷方式。

#!/bin/sh

MESSAGE="Hello world"

zenity --help > /dev/null
if [ 0 = $? ]; then
    zenity --info --text="$MESSAGE"
else
    dialog --no-shadow --infobox "$MESSAGE" 3 20
fi

zenity 是一個基於 Gtk+ 的視窗程式,當其執行時會先初始 GTK 環境。 若無法初始 GTK 環境時,將結束且結束碼為 1。 可據此判斷其是否執行於桌面環境。

dialog 使用範例

dialog 說明請參閱 dialog manpage。另推薦臥龍小三所著《Linux Shell 程式設計實務》一書,第十六章有許多精彩範例。

取得使用者的輸入結果

dialog 主要由程式結束碼(exit code)返回使用者輸入結果。 按 Unix shell script 慣例,0 表示正常、肯定等正面意義。 非零值(通常是1)皆表示錯誤、否定等錯誤意義。 若輸入結果為字串形式,則預設上將寫入標準錯誤輸出端(stderr)。 一般皆將其重導入暫存性檔案,再自檔案中讀出使用者輸入的內容。

dialog 可改變結果的輸出管道,但一般不建議這麼做。

dialog --yesno "是否繼續" 5 40
if [ $? -eq 0 ]; then
    echo "確定"
else
    echo "取消"
fi

dialog --textbox /usr/share/doc/libc6/copyright 20 60
if [ $? -eq 0 ]; then
    echo "接受"
else
    echo "不接受"
fi

使用者可按下 Ctrl-C 中止,此時將得到否定的程式結束碼(1)。

dialog --inputbox 請輸入文字 8 40 2> /dev/shm/dialog-output
text=`cat /tmp/dialog-output`
echo You input "$text".

新版 Linux 系統(大概從 Linux kernel 2.4 以後)會自動建立一個基於共享記憶體的檔案系統,掛載在 /dev/shm 。簡單地說,那是一塊 Ram disk,但使用上比 Ram disk 更方便。在其中建立檔案,便等於配置相同大小的共享記憶體區域,程序員甚至不需要用到 mmap() 或 shmget();刪除檔案即釋出記憶體。

處理暫存性資料時,/dev/shm 的效能遠優於傳統的 /tmp。 但對於不支援 /dev/shm 的傳統系統,仍建議將暫存性資料存於 /tmp 。

由標準輸入端控制 dialog 行為

這類使用元件必須透過標準輸入端輸入資料,才能控制它的動作。 例如 gauge, progressbox。

for i in 1 2 3 4 5 6 7 8 9 10 ; do
    case $i in
    1)
        # setp 1... do something.
        sleep 1
        ;;
    2)
        # setp 2... do something.
        sleep 1
        ;;
    3)
        # setp n... do something.
        # 以下類推
        sleep 1
        ;;
    *)
        # setp n... do something.
        sleep 1
        ;;
    esac;

    echo "${i}0" # write new percentage to zentiy
done \
| dialog --gauge Installing... 6 40

if [ $? -ne 0 ]; then
    echo "Cancel."
    exit 1
fi

echo "Normal end. If you cancel, here will not execute."

zenity 使用範例

GNOME zenity 完整說明請參閱 zenity manpage,或線上版:《Zenity Manual 》。

由程式結束碼返回使用者輸入結果

這類使用元件,由程式結束碼(exit code)返回使用者輸入結果。按 Unix shell script 慣例,0 表示正常、肯定等正面意義。 非零值(通常是1)皆表示錯誤、否定等錯誤意義。 question, text-info 屬於此類元件。

zenity --question
if [ $? -eq 0 ]; then
    echo "確定"
else
    echo "取消"
fi

zenity --title=本程式授權書 --text-info \
  --filename=/usr/share/doc/libc6/copyright --width=600 --height=600

if [ $? -eq 0 ]; then
    echo "接受"
else
    echo "不接受"
fi

雖然 text-info 元件沒有否定的按鈕,但若按下視窗標題區的 X 鈕,仍會得到否定的程式結束碼(1)。

由標準輸出端返回使用者輸入結果

這類使用元件,由標準輸出端返回使用者輸入結果。 大部份元件屬於此類。

text=`zenity --entry --title=請輸入文字`
echo You input "$text".

filepath=`zenity --file-selection --title=選擇檔案`
echo You select "$filepath".

選項 --directory 改為選擇目錄。--multiple 則可複選。

da=`zenity --calendar --date-format=%F`
echo You choice $da

--date-format 所接受的日期格式,與指令 date 相同。

由標準輸入端控制 zenity 的行為

這類使用元件有一部份參數,必須透過標準輸入端輸入,才能控制它的動作。 例如 list, progress。

item=`ls | zenity --list --column=List`
if [ $? -eq 0 ]; then
  echo You check $item.
else
  echo Cancel.
fi
for i in 1 2 3 4 5 6 7 8 9 10 ; do
    case $i in
    1)
        # setp 1... do something.
        sleep 1
        ;;
    2)
        # setp 2... do something.
        sleep 1
        ;;
    3)
        # setp n... do something.
        # 以下類推
        sleep 1
        ;;
    *)
        # setp n... do something.
        sleep 1
        ;;
    esac;

    echo "${i}0" # write new percentage to zentiy
done \
| zenity --progress --text="Installing..." --auto-close --auto-kill

if [ $? -ne 0 ]; then
    echo "Cancel."
    exit 1
fi

echo "Normal end. If you cancel, here will not execute."

樂多舊網址: http://blog.roodo.com/rocksaying/archives/15333309.html

樂多舊回應
maxubuntu@gmail.com(maxsolar) (#comment-24722834)
Fri, 20 Jun 2014 18:28:19 +0800
版主您好:

謝謝您的詳盡介紹!想請問hello-dialog.sh這隻script裡,
if [ 0 = $? ]; then

if [ $? = 0 ]; then
是不一樣的結果。請問您知道是為什麼嗎?
未留名 (#comment-24743765)
Mon, 23 Jun 2014 11:26:25 +0800
請問結果有什麼不同嗎?
我試了一下,都一樣。

另外,數值比對用 [ $? -eq 0 ] 或 [ 0 -eq $? ] 比較好。
hello-dialog.sh 是我寫的時候沒注意才用 = ;其他的例子都是用 -eq 比。