第 12 章 編程

目录

12.1. Shell 腳本
12.1.1. POSIX shell 兼容性
12.1.2. Shell 引數
12.1.3. Shell conditionals
12.1.4. shell 迴圈
12.1.5. shell 命令列的處理順序
12.1.6. 用於 shell 指令碼的應用程式
12.1.7. shell 指令碼對話方塊
12.1.8. zenity 的 shell 指令碼案例
12.2. make
12.3. C
12.3.1. 簡單的 C 程式(gcc)
12.4. 除錯
12.4.1. 基本的 gdb 使用命令
12.4.2. 除錯 Debian 軟體包
12.4.3. Obtaining backtrace
12.4.4. 高階 gdb 命令
12.4.5. Debugging X Errors
12.4.6. 檢查庫依賴性
12.4.7. 記憶體洩漏檢測工具
12.4.8. 靜態程式碼分析工具
12.4.9. 反彙編二進位制程式
12.5. Flex — 一個更好的Lex
12.6. Bison — a better Yacc
12.7. Autoconf
12.7.1. 編譯並安裝程式
12.7.2. 解除安裝程式
12.8. Perl short script madness
12.9. Web
12.10. 原始碼轉換
12.11. 製作 Debian 包

這裏我給出一些 Debian 系統中的信息,幫助學習編程的人找出打包的源代碼。下面是值得關注的軟件包和與之對應的文檔。

表 12.1. 幫助編程的軟件包清單

軟件包 流行度 大小
autoconf V:29, I:226 1868 autoconf-doc 包提供的“info autoconf
automake V:27, I:220 1707 automake1.10-doc 包提供的“info automake
bash V:853, I:999 5799 bash-doc 包提供的“info bash
bison V:10, I:113 2061 bison-doc 包提供的“info bison
cpp V:394, I:806 41 cpp-doc 包提供的“info cpp
ddd V:1, I:13 3965 ddd-doc 包提供的“info ddd
exuberant-ctags V:7, I:38 333 exuberant-ctags(1)
flex V:10, I:101 1174 flex-doc 包提供的“info flex
gawk V:355, I:478 2199 gawk-doc 包提供的“info gawk
gcc V:148, I:606 43 gcc-doc 包提供的“info gcc
gdb V:21, I:140 7983 gdb-doc 包提供的“info gdb
gettext V:53, I:367 7076 gettext-doc 包提供的“info gettext
gfortran V:20, I:63 16 gfortran-doc 包提供的“info gfortran”(Fortran 95)
fpc I:4 113 fpc(1) 和由 fp-docs 包提供的 html 文檔(Pascal)
glade V:1, I:12 2209 通過 UI Builder 菜單提供的文檔
libc6 V:933, I:999 10679 通過 glibc-docglibc-doc-reference 提供的“info libc
make V:154, I:622 1211 通過 make-doc 包提供的“info make
xutils-dev V:2, I:18 1466 imake(1)xmkmf(1) 等。
mawk V:371, I:997 183 mawk(1)
perl V:610, I:996 651 perl(1) 以及通過 perl-docperl-doc-html 提供的 html 文檔
python V:683, I:988 648 python(1) 以及通過 python-doc 包提供的 html 文檔
tcl8.4 V:3, I:50 NOT_FOUND tcl(3) 以及通過 tcl8.4-doc 包提供的更詳細的手冊頁文檔
tk8.4 V:1, I:31 NOT_FOUND tk(3) 以及通過 tk8.4-doc 包提供的更詳細的手冊頁文檔
ruby V:103, I:321 38 ruby(1) 以及通過 ri 包提供的交互式參考手冊
vim V:118, I:393 2374 通過 vim-doc 包提供的幫助(F1)菜單
susv2 I:0 15 通過“單一UNIX規範(版本2)”獲取(英語文檔)
susv3 I:0 15 通過“單一UNIX規範(版本3)”獲取(英語文檔)

安裝 manpagesmanpages-dev 包之後,可以通過運行“man 名稱”查看手冊頁中的參考信息。安裝了 GNU 工具的相關文檔包之後,可以通過運行“info 程序名稱”查看參考文檔。某些 GFDL 協議的文檔與 DFSG 並不兼容,所以你可能需要在 main 倉庫中包含 contribnon-free 才能下載並安裝它們。

[警告] 警告

不要用“test”作爲可執行的測試文件的名字,因爲 shell 中內建有“test”命令。

[小心] 小心

你可以把從源代碼編譯得到的程序直接放到“/usr/local”或“/opt”目錄,這樣可以避免與系統程序撞車。

[提示] 提示

“歌曲:99瓶啤酒”的代碼示例可以給你提供實踐各種語言的好範本。

Shell 腳本 是指包含有下面格式的可執行的文本文件。

#!/bin/sh
…… 命令

第一行指明瞭讀取並執行這個文件的 shell 解釋器。

讀懂 shell 腳本的最好 辦法是先理解類 UNIX 系統是如何工作的。這裏有一些 shell 編程的提示。看看“Shell 錯誤”(http://www.greenend.org.uk/rjk/2001/04/shell.html),可以從錯誤中學習。

不像 shell 交互模式(參見第 1.5 节 “簡單 shell 命令”第 1.6 节 “類 Unix 的文本處理”),shell 腳本會頻繁使用參數、條件和循環等。

Each command returns an exit status which can be used for conditional expressions.

  • Success: 0 ("True")

  • Error: non 0 ("False")

[注意] 注意

"0" in the shell conditional context means "True", while "0" in the C conditional context means "False".

[注意] 注意

"[" is the equivalent of the test command, which evaluates its arguments up to "]" as a conditional expression.

Basic conditional idioms to remember are the following.

  • "<command> && <if_success_run_this_command_too> || true"

  • "<command> || <if_not_success_run_this_command_too> || true"

  • A multi-line script snippet as the following

if [ <conditional_expression> ]; then
 <if_success_run_this_command>
else
 <if_not_success_run_this_command>
fi

Here trailing "|| true" was needed to ensure this shell script does not exit at this line accidentally when shell is invoked with "-e" flag.



算術整數的比較在條件表示式中為 "-eq","-ne","-lt","-le","-gt" 和 "-ge"。

下面是一個簡單的指令碼,它通過 dvdisaster(1) 建立了帶有 RS02 補充資料的 ISO 映像。

#!/bin/sh -e
# gmkrs02 : Copyright (C) 2007 Osamu Aoki <osamu@debian.org>, Public Domain
#set -x
error_exit()
{
  echo "$1" >&2
  exit 1
}
# Initialize variables
DATA_ISO="$HOME/Desktop/iso-$$.img"
LABEL=$(date +%Y%m%d-%H%M%S-%Z)
if [ $# != 0 ] && [ -d "$1" ]; then
  DATA_SRC="$1"
else
  # Select directory for creating ISO image from folder on desktop
  DATA_SRC=$(zenity --file-selection --directory  \
    --title="Select the directory tree root to create ISO image") \
    || error_exit "Exit on directory selection"
fi
# Check size of archive
xterm -T "Check size $DATA_SRC" -e du -s $DATA_SRC/*
SIZE=$(($(du -s $DATA_SRC | awk '{print $1}')/1024))
if [ $SIZE -le 520 ] ; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for CD backup:\\n $SIZE MB"
elif [ $SIZE -le 3500 ]; then
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is good for DVD backup :\\n $SIZE MB"
else
  zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
    --text="The data size is too big to backup : $SIZE MB"
  error_exit "The data size is too big to backup :\\n $SIZE MB"
fi
# only xterm is sure to have working -e option
# Create raw ISO image
rm -f "$DATA_ISO" || true
xterm -T "genisoimage $DATA_ISO" \
  -e genisoimage -r -J -V "$LABEL" -o "$DATA_ISO" "$DATA_SRC"
# Create RS02 supplemental redundancy
xterm -T "dvdisaster $DATA_ISO" -e  dvdisaster -i "$DATA_ISO" -mRS02 -c
zenity --info --title="Dvdisaster RS02" --width 640  --height 400 \
  --text="ISO/RS02 data ($SIZE MB) \\n created at: $DATA_ISO"
# EOF

你可能想要在桌面建立一個啟動器,其中的命令設定為類似 “/usr/local/bin/gmkrs02 %d” 的形式。

Make 是一個維護程式組的工具。一旦執行 make(1)make 會讀取規則檔案 Makefile,自從上次目標檔案被修改後,如果目標檔案依賴的相關檔案發生了改變,那麼就會更新目標檔案,或者目標檔案不存在,那麼這些檔案更新可能會同時發生。

規則檔案的語法如下所示。

目標:[相關檔案 ...]
[TAB] 命令1
[TAB] -命令2 # 忽略錯誤
[TAB] @命令3 # 禁止回顯

Here "[TAB]" is a TAB code. Each line is interpreted by the shell after make variable substitution. Use "\" at the end of a line to continue the script. Use "$$" to enter "$" for environment values for a shell script.

目標跟相關檔案也可以通過隱式規則給出,例如,如下所示。

%.o: %.c header.h

在這裡,目標包含了 "%" 字元 (只是它們中確切的某一個)。"%" 字元能夠匹配實際的目標檔案中任意一個非空的子串。相關檔案同樣使用 "%" 來表明它們是怎樣與目標檔案建立聯絡的。



執行 "make -p -f/dev/null" 命令來檢視內部自動化的規則。

你可以通過下列方法設定適當的環境來編譯使用 C 程式語言編寫的程式。

# apt-get install glibc-doc manpages-dev libc6-dev gcc build-essential

libc6-dev 軟體包,即 GNU C 庫,提供了 C 標準庫,它包含了 C 程式語言所使用的標頭檔案和庫例程。

參考資訊如下。

  • info libc”(C 庫函式參考)

  • gcc(1) 和 “info gcc

  • each_C_library_function_name(3)

  • Kernighan & Ritchie,“C 程式設計語言”,第二版(Prentice Hall)

除錯是程式中很重要的一部分。知道怎樣去除錯程式使得作為 Debian 使用者的你, 能夠做出有意義的錯誤報告。

Debian 上原始的偵錯程式gdb(1), 它能讓你在程式執行的時候檢查程式。

讓我們通過如下所示的命令來安裝 gdb 及其相關程式。

# apt-get install gdb gdb-doc build-essential devscripts

gdb 的好的教程由 "info gdb" 提供或者可以在網上的其他地方找到。如下是用 gdb(1) 在"程式"帶有 "-g" 選項編譯的時候來產生除錯資訊。

$ gdb program
(gdb) b 1                # 在第一行設定斷點
(gdb) run args           # 帶引數執行程式
(gdb) next               # 執行下一步
...
(gdb) step               # 單步進入
...
(gdb) p parm             # 列印 parm 的值
...
(gdb) p parm=12          # 把值設為 12
...
(gdb) quit
[提示] 提示

許多 gdb(1) 命令都能被縮寫。Tab 擴充套件跟在 shell 一樣都能工作。

Flex is a Lex-compatible fast lexical analyzer generator.

Tutorial for flex(1) can be found in "info flex".

You need to provide your own "main()" and "yywrap()". Otherwise, your flex program should look like this to compile without a library. This is because that "yywrap" is a macro and "%option main" turns on "%option noyywrap" implicitly.

%option main
%%
.|\n    ECHO ;
%%

Alternatively, you may compile with the "-lfl" linker option at the end of your cc(1) command line (like AT&T-Lex with "-ll"). No "%option" is needed in this case.

Several packages provide a Yacc-compatible lookahead LR parser or LALR parser generator in Debian.


Tutorial for bison(1) can be found in "info bison".

You need to provide your own "main()" and "yyerror()". "main()" calls "yyparse()" which calls "yylex()", usually created with Flex.

%%

%%

Autoconf is a tool for producing shell scripts that automatically configure software source code packages to adapt to many kinds of Unix-like systems using the entire GNU build system.

autoconf(1) produces the configuration script "configure". "configure" automatically creates a customized "Makefile" using the "Makefile.in" template.

Although any AWK scripts can be automatically rewritten in Perl using a2p(1), one-liner AWK scripts are best converted to one-liner Perl scripts manually.

Let's think following AWK script snippet.

awk '($2=="1957") { print $3 }' |

This is equivalent to any one of the following lines.

perl -ne '@f=split; if ($f[1] eq "1957") { print "$f[2]\n"}' |
perl -ne 'if ((@f=split)[1] eq "1957") { print "$f[2]\n"}' |
perl -ne '@f=split; print $f[2] if ( $f[1]==1957 )' |
perl -lane 'print $F[2] if $F[1] eq "1957"' |
perl -lane 'print$F[2]if$F[1]eq+1957' |

The last one is a riddle. It took advantage of following Perl features.

  • The whitespace is optional.

  • The automatic conversion exists from number to the string.

See perlrun(1) for the command-line options. For more crazy Perl scripts, Perl Golf may be interesting.

Basic interactive dynamic web pages can be made as follows.

  • Queries are presented to the browser user using HTML forms.

  • Filling and clicking on the form entries sends one of the following URL string with encoded parameters from the browser to the web server.

    • "http://www.foo.dom/cgi-bin/program.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/cgi-bin/program.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

    • "http://www.foo.dom/program.php?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3"

  • 在 URL 裡面 "%nn" 是使用一個 16 進位制字元的 nn 值代替。

  • 環境變數設定為: "QUERY_STRING="VAR1=VAL1 VAR2=VAL2 VAR3=VAL3"".

  • CGI program (any one of "program.*") on the web server executes itself with the environment variable "$QUERY_STRING".

  • CGI 程式的 stdout傳送到瀏覽器,作為互動式的動態 web 頁面展示。

For security reasons it is better not to hand craft new hacks for parsing CGI parameters. There are established modules for them in Perl and Python. PHP comes with these functionalities. When client data storage is needed, HTTP cookies are used. When client side data processing is needed, Javascript is frequently used.

更多資訊,參見 通用閘道器介面, Apache 軟體基金會, 和 JavaScript.

Searching "CGI tutorial" on Google by typing encoded URL http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial directly to the browser address is a good way to see the CGI script in action on the Google server.

原始碼轉換程式。


如果你想製作一個 Debian 包,閱讀下面內容。

debmake, dh-make, dh-make-perl 等軟體包,對軟體包打包過程,也有幫助。