Linux Shell下 的 萤 幕 显 示 控 制


王 国 泰 ( Kuotai Wang)

Linux 有 何 魅 力 ? 竟 能 让 有 人 得 奖 不 要 MediaStudio, 而 恳 求 只 要 这 薄 薄 一 本 "X Windows"? 随 着 本 专 栏 走 来 , 相 信 您 必 能 逐 步 了 解 她 的 能 耐 所 在 , 就 如 这 期 在 Shell下 能 有 何 显 示 控 制 ? 便 更 能 彰 显 出 Linux的 好 用 与 强 大 之 处 ( 国 泰 兄 语 ) 。

    在 UNIX系 统 中 撰 写 一 个 Shell Script是 一 件 常 常 碰 到 的 事 。 有 时 候 基 於 美 观 的 要 求 、 凸 显 讯 息 的 需 求 等 等 因 素 , 我 们 会 希 望 在 Shell Script 中 , 对 於 显 示 输 出 的 文 字 能 稍 作 控 制 来 达 到 这 样 的 效 果 。 基 本 上 , 在 Shell Script 中 可 以 透 过 一 些 工 具 程 式 来 改 变 终 端 机 输 出 的 文 字 属 性 , 譬 如 说 反 白 、 闪 烁 、 底 线 、 或 是 游 标 位 置 等 等 , 在 Linux 系 统 中 也 有 不 少 这 类 工 具 程 式 可 供 使 用 者 来 利 用 , 而 这 也 是 本 次 所 要 谈 的 主 题 。

    在 内 容 中 , 我 们 将 提 到 使 用 ” tput” 来 控 制 输 出 文 字 属 性 的 方 法 、 以 及 介 绍 说 明 一 个 能 够 在 Shell Script中 轻 轻 松 松 就 可 以 设 计 出 相 当 漂 亮 的 使 用 者 界 面 的 工 具 程 式 --” dialog” 的 使 用 方 法 。 正 所 谓 「 工 欲 善 其 事 必 先 利 其 器 」 , 有 了 这 些 工 具 的 帮 忙 将 会 使 Shell Script 展 现 出 更 为 多 彩 多 姿 的 外 衣 。

先 从 terminfo谈 起

    在 说 明 tput 的 使 用 方 法 之 前 , 我 们 必 须 先 为 读 者 介 绍 一 下 terminfo。 Terminfo 是 一 个 集 合 各 种 终 端 机 功 能 定 义 描 述 的 资 料 库 。 因 为 可 能 由 於 终 端 机 的 制 造 商 不 同 或 是 规 格 不 同 而 造 成 终 端 机 的 输 出 / 输 入 控 制 码 有 所 差 异 , 而 这 些 的 差 异 也 可 能 会 造 成 在 A牌 终 端 机 上 发 展 的 系 统 , 却 无 法 顺 利 在 B牌 终 端 机 上 显 示 。

    基 於 这 样 的 需 求 , Terminfo首 先 定 义 了 许 多 保 留 字 , 每 一 个 项 目 分 别 代 表 终 端 机 的 某 一 项 功 能 ( terminal capability) , 然 後 对 於 每 一 种 会 使 用 到 的 终 端 机 型 式 则 在 terminfo 中 建 立 一 个 该 型 号 终 端 机 能 力 描 述 的 记 录 。 在 这 个 资 料 记 录 里 , 会 对 於 Terminfo 所 定 义 每 一 个 的 项 目 指 定 一 个 真 正 控 制 终 端 机 实 际 动 作 的 控 制 码 、 或 是 属 性 设 定 。 这 样 一 来 , 一 般 应 用 程 式 在 碰 到 要 对 终 端 机 输 出 入 控 制 时 , 只 要 利 用 Terminfo 所 提 供 的 描 述 项 目 来 控 制 , 那 麽 在 执 行 时 , 便 会 依 据 Terminfo的 终 端 机 控 制 描 述 资 料 来 进 行 该 终 端 机 的 输 出 入 控 制 。

    下 面 是 Linux主 控 台 的 Terminfo 原 始 描 述 内 容 :

console|Standard Linux Console,
cr=^M, cud1=^J, ind=^J, bel=^G, il1=\E[L, am, cub1=^H, ed=\E[J,
el=\E[K, clear=\E[H\E[J, km, eo, mir, msgr, xon,
colors#8, pairs#64, lines#25, cols#80,
hpa=\E%i%p1%dG, vpa=\E%i%p1%dd, ri=\EM, hts=\EH, tbc=\E[3g,
smir=\E[4h, rmir=\E[4l, civis=\E[?25l, cnorm=\E[?25h,
sc=\E7, rc=\E8,
cup=\E[%i%p1%d;%p2%dH, op=\E[37m\E[40m,
dch=\E[%p1%dP, dl=\E[%p1%dM, home=\E[H, it#4, ich=\E[%p1%d@,
bold=\E[1m, rev=\E[7m, blink=\E[5m,
setf=\E[%p1%{30}%+%dm, setb=\E[%p1%{40}%+%dm,
kcuu1=\E[A, kcud1=\E[B, kcub1=\E[D, kcuf1=\E[C, khome=\E[H,
cuf1=\E[C, ht=^I, cuu1=\E[A,
smacs=\E(B\E)U^N,rmacs=\E(B\E)0^O,
rmul=\E[24m, smul=\E[4m, rmso=\E[0m, smso=\E[7m,
kf1=\E[[A, kf2=\E[[B, kf3=\E[[C, kf4=\E[[D, kf5=\E[[E,
kf6=\E[17~, kf7=\E[18~, kf8=\E[19~, kf9=\E[20~, kf10=\E[21~,
kdch1=\E[3~, kend=\E[4~, khome=\E[1~, knp=\E[6~, kpp=\E[5~,

    概 括 来 说 Terminfo 中 对 於 终 端 机 的 描 述 方 式 可 以 分 为 3种 :

布 林 值 : 直 接 写 上 能 力 名 称 , 即 代 表 此 终 端 机 有 此 能 力 。

xon ( 代 表 使 用 xon/xoff传 输 协 定 )

数 值 : 则 在 能 力 名 称 之 後 加 「 # 」 , 再 加 数 值 来 代 表 。

cols#80 ( 代 表 此 终 端 机 的 列 数 是 80 )

字 串 值 : 则 是 在 能 力 名 称 之 後 加 「 = 」 , 再 加 一 些 字 串 来 描 述 。

cub1=^H ( 代 表 CTRL-H是 游 标 往 左 移 动 一 格 )

    终 端 机 能 力 经 过 这 样 的 建 立 後 , 虽 然 不 同 的 终 端 机 同 一 个 行 为 的 控 制 码 不 相 同 , 但 是 一 般 程 式 使 用 时 却 只 要 给 予 该 输 出 入 行 为 的 名 称 ( 例 如 : blink 表 示 前 景 显 示 字 串 闪 烁 ) , 然 後 透 过 ” Terminfo” 的 转 换 取 得 真 正 的 终 端 机 控 制 码 送 往 终 端 机 设 备 。

    总 算 在 这 里 将 terminfo 大 致 上 做 了 一 点 说 明 , 希 望 各 位 已 经 有 一 点 点 概 念 了 , 由 於 关 於 终 端 机 属 性 的 内 容 则 实 在 是 非 常 繁 多 不 能 够 在 此 一 一 说 明 , 读 者 可 以 在 Linux系 统 中 执 行 man terminfo 取 得 相 关 的 说 明 。 因 为 接 下 来 我 们 要 谈 的 tput, dialog都 与 terminfo有 相 当 密 切 的 关 系 。

使 用 tput控 制 字 串 输 出

    tput这 个 程 式 就 是 直 接 利 用 terminfo 来 进 行 终 端 机 的 输 出 控 制 , 使 用 者 在 执 行 时 , 将 要 使 用 的 终 端 机 能 力 描 述 项 目 、 连 同 所 需 参 数 传 入 , 然 後 tput 便 会 根 据 这 些 传 入 值 进 行 终 端 机 的 输 出 控 制 。 基 本 的 使 用 格 式 :

    tput 终 端 机 能 力 描 述 [参 数 ]

    比 方 说 我 们 要 让 终 端 机 输 出 字 串 的 属 性 设 为 闪 烁 , 首 先 我 们 找 到 闪 烁 属 性 在 ” terminfo” 中 的 能 力 描 述 项 目 是 blink。 於 是 乎 , 我 们 就 可 以 执 行 ” tput” 来 设 定 终 端 机 显 示 闪 烁 字 元 。

    simon98:~$ tput blink

    或 是 要 将 显 示 的 前 景 /背 景 颜 色 互 换 前 执 行

    simon98:~$ tput rev

    甚 至 要 发 出 哔 一 声 , 也 可 以 利 用 ” tput” 来 达 到 效 果 。

    simon98:~$ tput bel

    下 面 是 一 个 ” tput” 小 小 的 使 用 例 子 , 读 者 可 以 经 由 这 个 例 子 更 了 解 ” tput” 的 使 用 方 法 。


       1    tput clear                                                

       2    line=`tput lines`                                         

       3    cols=`tput cols`                                          

       4    topX=2                                                    

       5    leftY=2                                                   

       6    botX=`expr $line - 3`                                     

       7    rightY=`expr $cols - 3 `                                  

       8    i=$leftY                                                  

       9    echo -ne "\016"                                           

       10   while [ $i -ne $rightY ]                                  

       11      do                                                     

       12        tput  cup $topX $i ; echo -n "q"                     

       13        tput  cup $botX $i ; echo -n "q"                     

       14        i=`expr $i + 1`                                      

       15      done                                                   

       16   tput cup  `expr $botX - 10`  1                            

       17   cat << EOF 18 19 aaaa 20 aa 21 aa aa a a aa aa 22 aa aaaaa a a aa aa 23 aa aa a a a a aaa 24 aa a aa a a a a aa aa 25 aaaaaaaa aaa a a aaaa aa aa 26 EOF 27 echo e "\017" 28 tput cup `expr $topX + 4` `expr $rightY / 2 6 ` 29 tput rev; echo n "W E L C O M E" 30 tput cup `expr $topX + 5` `expr $rightY / 2 6 ` 31 tput sgr0; echo n "-------------" 32 tput cup `expr $botX + 1` `expr $rightY / 2 6 ` 33 echo n "Now Date is `date`" 34 tput cup `expr $botX + 2` 1 35 echo n "Press any key to continue ...." 36 read $ans 

    例 子 中 使 用 到 的 ” tput” 说 明


   ①   tput lines        传 回 画 面 行 数 

   ②   tput cols         传 回 画 面 列 数 

   ③   tput cup  x y     将 游 标 移 至  x,y

   ④   tput rev          画 面 输 出 反 相 

   ⑤   tput sgr0         恢 复 预 设 值 

   ⑥   tput clear        清 除 画 面 内 容 

下 面 是 执 行 的 结 果 :




  阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐

 

 

 

                                  W E L C O M E

                                  -------------

 

 

 

 

 

    控控

    控

    控       控        *  * 控   控

    控          控控*  *  *  控 控

    控       控  *  *  *  *   控*

    控    *  控  *  *  *  *  控 控

   控控控控 控*  *  *  控控 控   控

 

 

 阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐阐

                                  Now Date is  Sun Aug  7 21:19:17 HKT 1994

 Press any key to continue ....





    由 於 tput 是 根 据 terminfo 的 内 容 来 控 制 终 端 机 , 所 以 所 使 用 的 终 端 机 能 力 描 述 项 目 一 定 要 在 terminfo 中 有 定 义 , 因 此 究 竟 有 哪 些 项 目 可 以 透 过 tput 来 控 制 , 不 妨 参 阅 terminfo 的 相 关 文 件 , 将 会 有 更 多 的 获 得 。

    虽 然 tput 使 用 上 相 当 容 易 , 要 应 付 简 易 的 字 串 输 出 控 制 是 绰 绰 有 馀 , 但 是 如 果 要 设 计 一 个 具 有 选 单 功 能 的 Shell Script 则 可 能 得 求 助 他 人 了 , 而 在 这 种 时 刻 ” dialog” 应 该 是 个 相 当 不 错 的 选 择 。

Shell中 的 对 话 盒 --dialog

    如 果 读 者 有 安 装 过 Slackware 1.2以 後 的 版 本 , 那 麽 应 该 对 於 Slackware所 显 现 的 彩 色 安 装 画 面 、 对 话 盒 式 的 交 谈 、 上 下 卷 动 选 项 等 等 的 功 能 感 到 非 常 的 Friendly 吧 ! 而 让 Slackware的 Install Script能 够 有 拥 有 这 样 一 个 效 果 的 因 素 是

    在 Install的 Shell Script中 使 用 ” dialog”

    dialog它 提 供 了 多 种 不 同 显 示 格 式 的 对 话 盒 功 能 , 让 使 用 者 能 够 在 Shell Script 中 利 用 它 来 进 行 使 用 者 界 面 的 输 出 入 控 制 。 目 前 dialog提 供 的 对 话 盒 形 式 有 下 列 七 种 : yes/no box, menu box, input box, message box, text box,info box 和 checklist box。 每 一 种 对 话 盒 都 有 他 不 同 的 使 用 者 输 入 控 制 , 所 以 设 计 者 可 以 针 对 自 己 的 需 要 来 选 择 适 用 的 格 式 。


yes/no box   :对 话 盒 有 供 使 

              用 者 输 入 。 

message box  :对 话 盒 仅 出 现 让 使 用 者 确 

              认 。 

info box     :对 话 盒 仅 显 示 出 讯 息 即 结 束 , 不 

              等 待 使 用 者 输 入 。 

menu box     :对 话 盒 中 显 示 多 个 选 项 , 可 以 利 

              用 游 标 上 下 移 动 选 择 其 中 一 个 结 

              果 。 

checklist box:与 menu box类 似 , 但 可 以 选 择 多 

              个 选 项 结 果 。 

text box     :显 示 某 个 档 案 的 内 容 , 可 以 上 下 

              页 卷 动 、 寻 找 字 串 等 。 

input box    :可 以 於 对 话 盒 中 让 使 用 者 输 入 字 

              串 值 。 

下 面 是 一 个 message box 的 使 用 例 子 :


01  dialog  --title "MESSAGE BOX" --clear \                               

02          --msgbox "Hi, this is a simple message box. You can use \     

03                    this to display any message you like. The box \     

04                    will remain until you press the ENTER key." 10 41   

05                                                                        

06  case $? in                                                            

07    0)                                                                  

08      echo "OK";;                                                       

09    255)                                                                

10      echo "ESC pressed.";;                                             

11  esac                                                                  



    执 行 结 果 显 示
MESSAGE BOX
Hi, this is a simple message box.

You can use this to display any

message you like. The box will

remain until you press the ENTER

key.

    从 这 个 例 子 里 , 我 们 可 以 看 到 dialog 基 本 的 使 用 架 构 。

    当 然 啦 !! 对 於 每 一 种 对 话 盒 格 式 他 的 参 数 传 入 也 有 些 不 同 的 差 异 。 使 用 ” dialog” 的 对 话 盒 参 数 传 入 格 式 如 下 所 示


--yesno       

--msgbox      

--infobox     

--inputbox    

--textbox     

--menu        

                     ...

--checklist   

                      ...

    前 面 所 提 的 是 如 何 选 用 dialog 的 对 话 盒 , 而 我 们 在 使 用 dialog 时 该 注 意 哪 一 些 大 原 则 以 及 如 何 取 得 dialog 所 接 收 到 使 用 者 的 输 入 值 或 是 按 键 选 项 , 这 些 内 部 的 处 理 方 式 大 致 上 可 以 条 列 出 下 列 几 点 供 读 者 做 参 考 :

1.要 在 对 话 盒 中 显 示 的 讯 息 或 是 选 项 文 字 的 内 容 , 由 Shell中 直 接 传 入 , 可 以 以 常 数 字 串 、 变 数 等 方 式 。

    "Hi, this is a simple message box."

或   "$TEXT"

或   "`cat input.file`"

2.对 於 萤 幕 的 输 出 与 输 入 部 份 , dialog 皆 是 透 过 呼 叫 Linux中 的 画 面 管 理 程 式 库 --ncurses 来 控 制 , 而 ncurses程 式 库 中 的 函 式 对 於 终 端 机 的 控 制 行 为 皆 是 使 用 terminfo 中 所 记 录 的 该 终 端 机 资 料 , 所 以 即 使 是 不 同 的 终 端 机 , 也 可 以 显 示 出 同 样 的 效 果 。

3.既 然 是 对 话 盒 , 因 此 就 会 有 使 用 者 部 份 的 输 入 行 为 , 而 对 於 这 些 使 用 者 的 输 入 , dialog 基 本 上 分 文 字 与 按 键 选 择 结 果 两 类 来 传 回 给 Shell这 一 层 。

    如 果 要 求 使 用 者 输 入 文 字 或 是 选 项 资 料 , 那 麽 ” dialog” 会 这 些 资 料 透 过 stderr( 错 误 输 出 设 备 ) 传 给 Shell, 所 以 设 计 者 可 以 将 stderr重 导 至 一 个 暂 存 档 或 Shell变 数 中 , 再 做 後 续 处 理 。 ( 如 inputbox, menubox, checklist 格 式 )


dialog   ...参 数 ...   2> /tmp/temp.$$

                            

        将 使 用 者 输 入 结 果 重 导 至 一 个 暂 存 档 中 

    如 果 按 下 的 是 某 一 个 跳 离 的 按 键 选 项 ( 如 OK, Cancel, Esc ) , 则 dialog 将 这 个 值 当 作 是 本 身 执 行 的 跳 离 值 ( Exit Code), 设 计 者 可 以 在 dialog结 束 执 行 後 , 立 即 在 Shell中 使 用 ” $?” 来 取 用 这 个 数 值 。


        dailog   ....参 数 ...



        case $?  in

           0)  ....

           .   ....

           .   ....

        esac

    讲 到 这 里 , 读 者 应 该 对 dialog有 一 个 更 多 的 了 解 吧 ? 由 於 篇 幅 有 限 所 以 也 不 能 一 口 气 将 所 有 的 Box使 用 范 例 都 在 这 里 讲 完 , 只 能 蜻 蜓 点 水 式 的 点 到 为 止 , 如 果 读 者 使 用 的 是 Slackware 1.2以 後 的 版 本 , 不 妨 参 阅 /usr/lib/setup中 Slackware的 安 装 Shell Script, 这 些 都 是 不 错 的 使 用 范 例 。 希 望 这 次 的 内 容 可 以 勾 起 各 位 的 一 点 点 注 意 力 、 一 些 些 好 奇 心 , 尝 试 的 去 试 试 这 个 不 错 的 工 具 程 式 , 让 写 出 来 的 Shell Script 更 为 多 彩 多 姿 , 各 位 互 相 勉 励 吧 !!