內容目錄
這裡我給出一些 Debian 系統中的資訊,幫助學習程式設計的人找出打包的原始碼。下面是值得關注的軟體包和與之對應的文件。
安裝 manpages
和 manpages-dev
包之後,可以通過運行“man 名稱
”查看手冊頁中的參考資訊。安裝了 GNU
工具的相關文檔包之後,可以通過運行“info 程序名稱
”查看參考文檔。某些 GFDL 協議的文檔與 DFSG
並不兼容,所以你可能需要在 main
倉庫中包含 contrib
和
non-free
才能下載並安裝它們。
請考慮使用版本控制系統工具。參見 節 10.5, “Git”。
![]() |
警告 |
---|---|
不要用“ |
![]() |
注意 |
---|---|
你可以把從源代碼編譯得到的程序直接放到“ |
![]() |
提示 |
---|---|
“歌曲:99瓶啤酒”的代碼示例可以給你提供實踐各種語言的好範本。 |
Shell 腳本 是指包含有下面格式的可執行的文本文件。
#!/bin/sh ... command lines
第一行指明瞭讀取並執行這個文件的 shell 解釋器。
讀懂 shell 腳本的最好 辦法是先理解類 UNI圖形化界面是如何工作的。這裏有一些 shell 編程的提示。看看“Shell 錯誤”(http://www.greenend.org.uk/rjk/2001/04/shell.html),可以從錯誤中學習。
不像 shell 交互模式(參見節 1.5, “簡單 shell 指令” 和 節 1.6, “類 Unix 的文本處理”),shell 腳本會頻繁使用參數、條件和循環等。
系統中的許多指令碼都可以透過任意 POSIX shell(參見 表格 1.13, “shell 程式列表”)來執行。
預設的非互動 POSIX shell "/bin/sh
" 是一個指向到
/usr/bin/dash
的符號連結,並被許多系統程式使用。
預設的互動式 POSIX shell 是 /usr/bin/bash
。
避免編寫具有 bashisms(bash 化)或者 zshisms(zsh 化)語法的 shell 指令碼,確保指令碼在所有 POSIX shell
之間具有可移植性。你可以使用 checkbashisms
(1) 對其進行檢查。
表格 12.1. 典型 bashism 語法列表
好的:POSIX | 應該避免的:bashism |
---|---|
if [ "$foo" = "$bar" ] ; then … |
if [ "$foo" == "$bar" ] ; then … |
diff -u file.c.orig file.c |
diff -u file.c{.orig,} |
mkdir /foobar /foobaz |
mkdir /foo{bar,baz} |
funcname() { … } |
function funcname() { … } |
八進位制格式:"\377 " |
十六進位制格式:"\xff " |
使用 "echo
" 指令的時候需要注意以下幾個方面,因為根據內建 shell 和外部指令的不同,它的實現也有差別。
避免使用除“-n
”以外的任何指令列選項。
避免在字串中使用轉義序列,因為根據 shell 不同,計算後的結果也不一樣。
![]() |
注意 |
---|---|
儘管“ |
![]() |
提示 |
---|---|
如果你想要在輸出字串中嵌入轉義序列,用 " |
特殊的 shell 參數經常在 shell 腳本里面被用到。
表格 12.2. shell 參數列表
shell 參數 | 值 |
---|---|
$0 |
shell 或 shell 指令碼的名稱 |
$1 |
第一個 shell 參數 |
$9 |
第 9 個 shell 參數 |
$# |
位置參數數量 |
"$*" |
"$1 $2 $3 $4 … " |
"$@" |
"$1" "$2" "$3" "$4" … |
$? |
最近一次指令的退出狀態碼 |
$$ |
這個 shell 指令碼的 PID |
$! |
最近開始的後臺任務 PID |
如下所示是需要記憶的基本的參數展開。
表格 12.3. shell 參數展開列表
參數表示式形式 | 如果 var 變數已設定那麼值為 |
如果 var 變數沒有被設定那麼值為 |
---|---|---|
${var:-string} |
"$var " |
"string " |
${var:+string} |
"string " |
"null " |
${var:=string} |
"$var " |
"string " (並執行 "var=string ") |
${var:?string} |
"$var " |
在 stderr 中顯示 "string "
(出錯退出) |
以上這些操作中 ":
" 實際上都是可選的。
有 ":
" 等於測試的 var
值是存在且非空
沒有 ":
" 等於測試的 var
值只是存在的,可以為空
表格 12.4. 重要的 shell 參數替換列表
參數替換形式 | 結果 |
---|---|
${var%suffix} |
刪除位於 var 結尾的 suffix 最小匹配模式 |
${var%%suffix} |
刪除位於 var 結尾的 suffix 最大匹配模式 |
${var#prefix} |
刪除位於 var 開頭的 prefix 最小匹配模式 |
${var##prefix} |
刪除位於 var 開頭的 prefix 最大匹配模式 |
每個指令都會回傳 退出狀態,這可以被條件語句使用。
成功:0 ("True")
失敗:非0 ("False")
![]() |
注意 |
---|---|
"0" 在 shell 條件語句中的意思是 "True",然而 "0" 在 C 條件語句中的含義為 "False"。 |
![]() |
注意 |
---|---|
" |
如下所示是需要記憶的基礎 條件語法。
"command &&
if_success_run_this_command_too || true
"
"command ||
if_not_success_run_this_command_too || true
"
如下所示是多行指令碼片段
if [ conditional_expression ]; then if_success_run_this_command else if_not_success_run_this_command fi
這裡末尾的“|| true
”是需要的,它可以保證這個 shell
指令碼在不小心使用了“-e
”選項而被呼叫時不會在該行意外地退出。
表格 12.5. 在條件表示式中進行檔案比較
表示式 | 回傳邏輯真所需的條件 |
---|---|
-e file |
file 存在 |
-d file |
file 存在並且是一個目錄 |
-f file |
file 存在並且是一個普通檔案 |
-w file |
file 存在並且可寫 |
-x file |
file 存在並且可執行 |
file1 -nt file2 |
file1 是否比 file2 新 |
file1 -ot file2 |
file1 是否比 file2 舊 |
file1 -ef file2 |
file1 和 file2 位於相同的裝置上並且有相同的 inode 編號 |
表格 12.6. 在條件表示式中進行字串比較
表示式 | 回傳邏輯真所需的條件 |
---|---|
-z str |
str 的長度為零 |
-n str |
str 的長度不為零 |
str1 = str2 |
str1 和 str2 相等 |
str1 != str2 |
str1 和 str2 不相等 |
str1 < str2 |
str1 排列在 str2 之前(取決於語言環境) |
str1 > str2 |
str1 排列在 str2 之後(取決於語言環境) |
算術整數的比較在條件表示式中為
"-eq
","-ne
","-lt
","-le
","-gt
"
和 "-ge
"。
這裡有幾種可用於 POSIX shell 的迴圈形式。
"for x in foo1 foo2 … ; do command ; done
",該迴圈會將
"foo1 foo2 …
" 賦予變數 "x
" 並執行
"command
"。
"while condition ; do command ; done
",當
"condition
" 為真時,會重複執行 "command
"。
"until condition ; do command ; done
",當
"condition
" 為假時,會重複執行 "command
"。
"break
" 可以用來退出迴圈。
"continue
" 可以用來重新開始下一次迴圈。
![]() |
提示 |
---|---|
C 語言中的數值迭代可以用 |
![]() |
提示 |
---|---|
普通的 shell 命令列提示下的一些常見的環境變數,可能在你的指令碼的執行環境中不存在。
對於 "$USER
", 使用 "$(id -un)
"
對於 "$UID
", 使用 "$(id -u)
"
對於 "$HOME
",使用"$(getent passwd "$(id -u)"|cut -d
":" -f 6)
" (這個也在 節 4.5.2, “現代的集中式系統管理” 下工作)
shell 大致以下列的順序來處理一個指令碼。
shell 讀取一行。
如果該行包含有"…"
或 '…'
,shell 對該行各部分進行分組作為
一個標識(one token) (譯註:one token 是指 shell
識別的一個結構單元).
shell 通過下列方式將行中的其它部分分隔進 標識(tokens)。
空白字元:空格 tab
換行符
元字元: | ; & ( )
shell 會檢查每一個不位於 "…"
或 '...'
的 token 中的
保留字 來調整它的行為。
保留字:if then elif else fi for in
while unless do done case esac
shell 展開不位於 "…"
或 '...'
中的 別名。
shell 展開不位於 "…"
或 '...'
中的 波浪線。
"~
" → 當前使用者的家目錄
"~user
" →
user
的家目錄
shell 將不位於 '...'
中的 變數
展開為它的值。
變數:"$PARAMETER
" 或
"${PARAMETER}
"
shell 展開不位於 '...'
中的 指令替換。
"$( command )
" → "command
" 的輸出
"` command `
" → "command
" 的輸出
shell 將不位於 "…"
或 '...'
中的 glob 路徑 展開為匹配的檔名。
*
→ 任何字元
?
→ 一個字元
[…]
→ 任何位於 "…
" 中的字元
shell 從下列幾方面查詢 指令 並執行。
函式 定義
內建指令
“$PATH
” 中的可執行檔案
shell 前往下一行,並按照這個順序從頭再次進行處理。
雙引號中的單引號是沒有效果的。
在 shell 中執行 “set -x
” 或使用 “-x
” 選項啟動
shell 可以讓 shell 顯示出所有執行的指令。這對除錯來說是非常方便的。
為了使你的 shell 程式在 Debian 系統上儘可能地具有可移植性,你應該只使用 必要的 軟體包所提供的應用程式。
"aptitude search ~E
",列出 必要的 軟體包。
"dpkg -L package_name |grep
'/man/man.*/'
",列出
package_name
軟體包所提供的 man 手冊。
表格 12.7. 包含用於 shell 指令碼的小型應用程式的軟體包
軟體包 | 流行度 | 大小 | 說明 |
---|---|---|---|
dash
|
V:896, I:994 | 222 | 小和快的 POSIX 相容 shell,用於 sh |
coreutils
|
V:898, I:999 | 17372 | GNU 核心工具 |
grep
|
V:815, I:999 | 1118 | GNU grep 、egrep 和
fgrep |
sed
|
V:804, I:999 | 912 | GNU sed |
mawk
|
V:392, I:997 | 247 | 小和快的 awk |
debianutils
|
V:924, I:999 | 242 | 用於 Debian 的各種工具 |
bsdutils
|
V:646, I:999 | 419 | 來自 4.4BSD-Lite 的基礎工具 |
bsdextrautils
|
V:304, I:392 | 422 | 來自 4.4BSD-Lite 的額外的工具 |
moreutils
|
V:12, I:37 | 244 | 額外的 Unix 工具 |
![]() |
提示 |
---|---|
儘管 |
參見 節 1.6, “類 Unix 的文本處理” 的例子。
表格 12.8. 直譯器相關軟體包列表
軟體包 | 流行度 | 大小 | 包 |
---|---|---|---|
dash
|
V:896, I:994 | 222 | sh: 小和快的 POSIX 相容的 shell,用於 sh |
bash
|
V:803, I:999 | 6450 | sh: 由 bash-doc
包提供的“info bash ” |
mawk
|
V:392, I:997 | 247 | AWK: 小和快的 awk |
gawk
|
V:334, I:417 | 2456 | AWK: 由 gawk-doc
包提供的“info gawk ” |
perl
|
V:611, I:991 | 719 | Perl: perl (1) 以及透過
perl-doc 和 perl-doc-html 提供的 html 文件 |
libterm-readline-gnu-perl
|
V:2, I:29 | 380 | GNU ReadLine/History 庫的 Perl 擴充套件: perlsh (1) |
libreply-perl
|
V:0, I:0 | 170 | Perl 的 REPL : reply (1) |
libdevel-repl-perl
|
V:0, I:0 | 237 | Perl 的 REPL : re.pl (1) |
python3
|
V:694, I:917 | 90 | Python: python3 (1) 以及透過
python3-doc 包提供的 html 文件 |
tcl
|
V:26, I:318 | 22 | Tcl: tcl (3) 以及透過
tcl-doc 包提供的更詳細的手冊頁文件 |
tk
|
V:26, I:310 | 22 | Tk:tk (3) 以及透過
tk-doc 包提供的更詳細的手冊頁文件 |
ruby
|
V:102, I:279 | 35 | Ruby: ruby (1),
erb (1), irb (1),
rdoc (1), ri (1) |
當你希望在 Debian 上自動化執行一個任務,你應當首先使用解釋性語言指令碼。選擇解釋性語言的準則是:
使用 dash
,如果任務是簡單的,使用 shell 程式聯合 CLI 命令列程式。
使用 python3
,如果任務不是簡單的,你從零開始寫。
使用
perl
、tcl
、ruby
……,如果在
Debian 上有用這些語言寫的現存程式碼,需要為完成任務進行調整。
如果最終程式碼太慢,為提升執行速度,你可以用編譯型語言重寫關鍵部分,從解釋性語言呼叫。
大部分直譯器提供基本的語法檢查和程式碼跟蹤功能。
“dash -n script.sh” - Shell 指令碼語法檢查
“dash -x script.sh” - 跟蹤一個 Shell 指令碼
“python -m py_compile script.py” - Python 指令碼語法檢查
“python -mtrace --trace script.py” - 跟蹤一個 Python 指令碼
“perl -I ../libpath -c script.pl” - Perl 指令碼語法檢查
“perl -d:Trace script.pl” - 跟蹤一個 Perl 指令碼
為測試 dash
程式碼,嘗試下 節 9.1.4, “Readline 封裝”,它提供了和 bash
類似的互動式環境。
為了測試 perl
程式碼,嘗試下 Perl 的 REPL 環境,它為 Perl 提供了 Python 類似的
REPL (=READ + EVAL + PRINT +
LOOP) 環境。
shell 指令碼能夠被改進用來製作一個吸引人的 GUI(圖形使用者介面)程式。技巧是用一個所謂的對話程式來代替使用
echo
和 read
命令的乏味互動。
表格 12.9. 對話(dialog )程式列表
軟體包 | 流行度 | 大小 | 說明 |
---|---|---|---|
x11-utils
|
V:169, I:581 | 712 | xmessage (1):在一個視窗中顯示一條訊息或疑問(X) |
whiptail
|
V:253, I:996 | 71 | 從 shell 指令碼中顯示使用者友好的對話方塊(newt) |
dialog
|
V:13, I:114 | 1217 | 從 shell 指令碼中顯示使用者友好的對話方塊(ncurses) |
zenity
|
V:71, I:381 | 167 | 從 shell 指令碼中顯示圖形對話方塊(GTK) |
ssft
|
V:0, I:0 | 75 | Shell 指令碼前端工具 (zenity, kdialog, and 帶有 gettext 的 dialog 封裝) |
gettext
|
V:49, I:292 | 5843 | “/usr/bin/gettext.sh ”:翻譯資訊 |
這裡是一個用來演示的 GUI 程式的例子,僅使用一個 shell 指令碼是多麼容易。
這個指令碼使用 zenity
來選擇一個檔案 (預設 /etc/motd
)
並顯示它。
這個指令碼的 GUI 啟動器能夠按 節 9.4.10, “從 GUI 啟動一個程式” 建立。
#!/bin/sh -e # Copyright (C) 2021 Osamu Aoki <osamu@debian.org>, Public Domain # vim:set sw=2 sts=2 et: DATA_FILE=$(zenity --file-selection --filename="/etc/motd" --title="Select a file to check") || \ ( echo "E: File selection error" >&2 ; exit 1 ) # Check size of archive if ( file -ib "$DATA_FILE" | grep -qe '^text/' ) ; then zenity --info --title="Check file: $DATA_FILE" --width 640 --height 400 \ --text="$(head -n 20 "$DATA_FILE")" else zenity --info --title="Check file: $DATA_FILE" --width 640 --height 400 \ --text="The data is MIME=$(file -ib "$DATA_FILE")" fi
這種使用 shell 指令碼的 GUI 程式方案只對簡單選擇的場景有用。如果你寫一個其它任何複雜的程式,請考慮在功能更強的平臺上寫。
GUI(圖形使用者介面)檔案管理器在選定的檔案上,能夠用外加的擴充套件軟體包來擴充套件執行一些常見行為。透過增加特定的指令碼,它們也能夠用來定製執行非常特殊的行為。
對於 GNOME,參見 NautilusScriptsHowto。
對於 KDE,參見 Creating Dolphin Service Menus。
對於 Xfce,參見 Thunar - Custom Actions 和 https://help.ubuntu.com/community/ThunarCustomActions。
對於 LXDE,參見 Custom Actions。
為了處理資料,sh
需要生成子程序執行
cut
、grep
、 sed
等,是慢的。從另外一個方面,perl
有內部處理資料能力,是快的。所以 Debian 上的許多系統維護指令碼使用
perl
。
讓我們考慮下面一行 AWK 指令碼片段和它在 Perl 中的等價物。
awk '($2=="1957") { print $3 }' |
這等價於下列的任意一行。
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' |
最後一個簡直就是個迷。它用上了下面列出的這些 Perl 的特性。
空格為可選項。
存在從數字到字串的自動轉換。
透過命令列選項: perlrun
(1) 的 Perl 執行技巧
Perl 特異變數:perlvar
(1)
靈活性是 Perl 的強項。與此同時,這允許我們建立令人困惑和繁亂的程式碼。所以請小心。
更多瘋狂的 Perl 指令碼,有興趣的可以看下 Perl Golf 。
表格 12.10. 編譯相關軟體包列表
軟體包 | 流行度 | 大小 | 說明 |
---|---|---|---|
gcc
|
V:145, I:580 | 45 | GNU C 編譯器 |
libc6-dev
|
V:233, I:596 | 13859 | GNU C 庫:開發庫和標頭檔案 |
g++
|
V:59, I:510 | 15 | GNU C++ 編譯器 |
libstdc++-10-dev
|
V:31, I:200 | 17575 | GNU 標準 C++ 庫 版本 3(開發檔案) |
cpp
|
V:322, I:748 | 42 | GNU C 預處理 |
gettext
|
V:49, I:292 | 5843 | GNU 國際化工具 |
glade
|
V:0, I:7 | 1332 | GTK 使用者介面構建器 |
valac
|
V:0, I:6 | 713 | 使用 GObject 系統類似 C# 的語言 |
flex
|
V:8, I:85 | 1279 | LEX 相容的 fast lexical analyzer generator |
bison
|
V:9, I:94 | 3111 | YACC 相容的 解析器生成器 |
susv2
|
I:0 | 16 | 通過“單一UNIX規範(版本2)”獲得(英語文檔) |
susv3
|
I:0 | 16 | 通過“單一UNIX規範(版本3)”獲得(英語文檔) |
golang
|
I:21 | 12 | Go 程式語言編譯器 |
rustc
|
V:2, I:13 | 9018 | Rust 系統程式語言 |
haskell-platform
|
I:5 | 12 | 標準的 Haskell 庫和工具 |
gfortran
|
V:10, I:86 | 16 | GNU Fortran 95 編譯器 |
fpc
|
I:3 | 121 | 自由 Pascal |
這裡,包括了 節 12.3.3, “Flex — 一個更好的 Lex” 和 節 12.3.4, “Bison — 一個更好的 Yacc”,用來說明 類似編譯器的程式怎樣用C 語言來編寫,是透過編譯高階描述到 C 語言。
你可以通過下列方法設定適當的環境來編譯使用 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)
一個簡單的例子 “example.c
” 可以通過如下方式和 “libm
”
庫一起編譯為可執行程式 “run_example
”。
$ cat > example.c << EOF #include <stdio.h> #include <math.h> #include <string.h> int main(int argc, char **argv, char **envp){ double x; char y[11]; x=sqrt(argc+7.5); strncpy(y, argv[0], 10); /* prevent buffer overflow */ y[10] = '\0'; /* fill to make sure string ends with '\0' */ printf("%5i, %5.3f, %10s, %10s\n", argc, x, y, argv[1]); return 0; } EOF $ gcc -Wall -g -o run_example example.c -lm $ ./run_example 1, 2.915, ./run_exam, (null) $ ./run_example 1234567890qwerty 2, 3.082, ./run_exam, 1234567890qwerty
為了使用 sqrt
(3),必須使用 “-lm
” 連結來自
libc6
軟體包的庫
“/usr/lib/libm.so
”。實際的庫檔案位於
“/lib/
”,檔名為 “libm.so.6
”,它是指向
“libm-2.7.so
” 的一個連結。
請看一下輸出文字的最後一段。即使指定了 “%10s
”,它依舊超出了 10 個字元。
使用沒有邊界檢查的指標記憶體操作函式,比如 sprintf
(3) 和
strcpy
(3), 是不建議使用,是為防止快取溢位洩露而導致上面的溢位問題。請使用
snprintf
(3) 和 strncpy
(3) 來替代.
可以使用 “info flex
” 檢視 flex
(1) 的教程。
你需要提供你自己的 "main()
" 和 "yywrap()
".否則,你的
flex 程式,看起來像這樣的,編譯的時候將不會帶庫。這是因為 "yywrap
" 是一個巨集,
"%option main
" 隱性打開了 "%option
noyywrap
".
%option main %% .|\n ECHO ; %%
另外一種方法,在你的 cc
(1)
指令列結尾,你可以使用編譯連結器選項,"-lfl
"。(像使用 "-ll
" 的
AT&T-Lex ). 在這種情況下,不需要 "%option
".
類似 Indent 的工具能夠幫助人進行程式碼檢查,透過一致性的重新格式化原始碼。
類似 Ctags 的工具能夠幫助人進行程式碼檢查,透過利用原始碼中發現的名字生成 索引(或標籤)檔案。
![]() |
提示 |
---|---|
配置你喜歡的編輯器( |
表格 12.12. 靜態程式碼分析工具的列表
軟體包 | 流行度 | 大小 | 說明 |
---|---|---|---|
vim-ale
|
I:0 | 2167 | 用於 Vim 8 和 NeoVim 的非同步 Lint 引擎 |
vim-syntastic
|
I:3 | 1240 | vim 語法檢查利器 |
elpa-flycheck
|
V:0, I:1 | 792 | Emacs 現代實時語法檢查 |
elpa-relint
|
I:0 | 133 | Emacs Lisp 正則錯誤發現器 |
cppcheck-gui
|
V:0, I:1 | 6253 | 靜態 C/C++ 程式碼分析工具(GUI) |
shellcheck
|
V:2, I:10 | 15883 | shell 指令碼的 lint 工具 |
pyflakes3
|
V:0, I:15 | 23 | Python 3 程式被動檢查器 |
pylint
|
V:4, I:17 | 1653 | Python 程式碼靜態檢查器 |
perl
|
V:611, I:991 | 719 | 帶有內部靜態程式碼檢測的直譯器:B::Lint (3perl) |
rubocop
|
V:0, I:0 | 2390 | Ruby 靜態程式碼分析器 |
clang-tidy
|
V:1, I:7 | 25 | 基於 clang 的 C++ 規則格式檢查工具 |
splint
|
V:0, I:3 | 2320 | 靜態檢查 C 程式 bug 的工具 |
flawfinder
|
V:0, I:0 | 205 | 檢查 C/C++ 原始碼和查詢安全漏洞的工具 |
black
|
V:1, I:6 | 559 | 強硬的 Python 程式碼格式化器 |
perltidy
|
V:0, I:5 | 2101 | Perl 指令碼縮排和重新格式化 |
indent
|
V:0, I:11 | 425 | C 語言原始碼格式化程式 |
astyle
|
V:0, I:3 | 761 | C、 C++、 Objective-C、 C# 和 Java 的原始碼縮排器 |
bcpp
|
V:0, I:0 | 110 | 美化 C(++) |
xmlindent
|
V:0, I:1 | 53 | XML 流 重新格式化 |
global
|
V:0, I:3 | 1896 | 原始碼檢索和瀏覽工具 |
exuberant-ctags
|
V:4, I:29 | 345 | 構建原始碼定義的標籤檔案索引 |
除錯是程式中很重要的一部分。知道怎樣去除錯程式,能夠讓你成為一個好的 Debian 使用者, 能夠做出有意義的錯誤報告。
Debian 上原始的偵錯程式是 gdb
(1),
它能讓你在程式執行的時候檢查程式。
讓我們通過如下所示的指令來安裝 gdb
及其相關程式。
# apt-get install gdb gdb-doc build-essential devscripts
好的 gdb
教程能夠被發現:
“info gdb
”
在 /usr/share/doc/gdb-doc/html/gdb/index.html
的 “Debugging
with GDB”
這裡是一個簡單的列子,用 gdb
(1) 在"程式
"帶有
"-g
" 選項編譯的時候來產生除錯資訊。
$ gdb program (gdb) b 1 # set break point at line 1 (gdb) run args # run program with args (gdb) next # next line ... (gdb) step # step forward ... (gdb) p parm # print parm ... (gdb) p parm=12 # set value to 12 ... (gdb) quit
![]() |
提示 |
---|---|
許多 |
Debian 系統在預設情況下,所有安裝的二進位制程式會被 stripped,因此大部分除錯符號(debugging
symbols)在通常的軟體包裡面會被移除。為了使用 gdb
(1) 除錯 Debian 軟體包,
*-dbgsym
軟體包需要被安裝。(例如,安裝
coreutils-dbgsym
,用於除錯coreutils
)原始碼軟體包和普通的二進位制軟體包一起自動生成
*-dbgsym
軟體包。那些除錯軟體包將被獨立放在 debian-debug 檔案庫。更多資訊請參閱 Debian Wiki 文件 。
如果一個需要被除錯的軟體包沒有提供其 *-dbgsym
軟體包,你需要按如下所示的從原始碼中重構並且安裝它。
$ mkdir /path/new ; cd /path/new $ sudo apt-get update $ sudo apt-get dist-upgrade $ sudo apt-get install fakeroot devscripts build-essential $ apt-get source package_name $ cd package_name* $ sudo apt-get build-dep ./
按需修改 bug。
軟體包除錯版本跟它的官方 Debian 版本不衝突,例如當重新編譯已存在的軟體包版本產生的 "+debug1
"
字尾,如下所示是編譯未發行的軟體包版本產生的 "~pre1
" 字尾。
$ dch -i
如下所示編譯並安裝帶有除錯符號的軟體包。
$ export DEB_BUILD_OPTIONS="nostrip noopt" $ debuild $ cd .. $ sudo debi package_name*.changes
你需要檢查軟體包的構建指令碼並確保編譯二進位制的時候使用了 "CFLAGS=-g -Wall
" 選項。
當你碰到程式崩潰的時候,報告 bug 時附上棧幀資訊是個不錯的注意。
使用如下方案之一,可以透過 gdb
(1) 取得棧幀資訊:
在 GDB 中崩潰的方案:
從 GDB 執行程式。
崩潰程式。
在 GDB 提示符輸入 "bt
"。
先奔潰的方案:
對於無限迴圈或者鍵盤凍結的情況,你可以透過按 Ctrl-\
或 Ctrl-C
或者執行 “kill -ABRT PID
” 強制奔潰程式。(參見
節 9.4.12, “殺死一個程序”)
![]() |
提示 |
---|---|
通常,你會看到堆疊頂部有一行或者多行有 " $ MALLOC_CHECK_=2 gdb hello |
表格 12.14. 高階 gdb 指令列表
指令 | 指令用途的描述 |
---|---|
(gdb) thread apply all bt |
得到多執行緒程式的所有執行緒棧幀 |
(gdb) bt full |
檢視函式呼叫棧中的參數資訊 |
(gdb) thread apply all bt full |
和前面的選項一起得到堆疊和參數 |
(gdb) thread apply all bt full 10 |
得到前10個呼叫的棧幀和參數資訊,以此來去除不相關的輸出 |
(gdb) set logging on |
把 gdb 的日誌輸出到檔案 (預設的是 "gdb.txt ") |
按如下所示使用 ldd
(1) 來找出程式的庫依賴性。
$ ldd /bin/ls librt.so.1 => /lib/librt.so.1 (0x4001e000) libc.so.6 => /lib/libc.so.6 (0x40030000) libpthread.so.0 => /lib/libpthread.so.0 (0x40153000) /lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)
因為 ls
(1) 執行在 `chroot`ed 環境,以上的庫在 `chroot`ed 環境也必須是可用的。
在 Debian 中,有幾個動態呼叫跟蹤工具存在。參見 節 9.4, “監控、控制和啟動程式活動”。
如果一個 GNOME 程式 preview1
收到了一個 X 錯誤,您應當看見一條下面這樣的資訊。
The program 'preview1' received an X Window System error.
如果就是這種情況,你可以嘗試在執行程式的時候加上 "--sync
" 選項,並且在
"gdk_x_error
" 函式處設定中斷來獲得棧幀資訊。
Debian 上有一些可用的記憶體洩漏檢測工具。
表格 12.15. 記憶體洩漏檢測工具的列表
軟體包 | 流行度 | 大小 | 說明 |
---|---|---|---|
libc6-dev
|
V:233, I:596 | 13859 | mtrace (1):除錯 glibc 中的 malloc |
valgrind
|
V:6, I:43 | 77249 | 記憶體偵錯程式和分析器 |
electric-fence
|
V:0, I:5 | 72 | malloc (e) 偵錯程式 |
libdmalloc5
|
V:0, I:3 | 393 | 記憶體分配庫除錯 |
duma
|
V:0, I:0 | 293 | 在 C 和 C++ 程式中檢測快取溢位和快取欠載( buffer under-runs )的庫 |
leaktracer
|
V:0, I:2 | 57 | C++ 程式記憶體洩露跟蹤器 |
表格 12.16. 編譯工具軟體包列表
軟體包 | 流行度 | 大小 | 包 |
---|---|---|---|
make
|
V:133, I:588 | 1592 | 通過 make-doc 包提供的“info make ” |
autoconf
|
V:29, I:270 | 2033 | 由 autoconf-doc 包提供的“info autoconf ” |
automake
|
V:28, I:268 | 1836 | 由 automake1.10-doc 包提供的“info automake ” |
libtool
|
V:22, I:253 | 1198 | 由 libtool-doc 包提供"info libtool " |
cmake
|
V:17, I:118 | 26660 | cmake (1) 跨平臺、開源的編譯系統 |
ninja-build
|
V:5, I:31 | 347 | ninja (1) 接近 Make 精髓的小編譯系統 |
meson
|
V:2, I:19 | 3255 | meson (1) 在 ninja 之上的高生產力的構建系統 |
xutils-dev
|
V:1, I:10 | 1485 | imake (1),xmkmf (1) 等。 |
Make 是一個維護程式組的工具。一旦執行
make
(1),make
會讀取規則檔案
Makefile
,自從上次目標檔案被修改後,如果目標檔案依賴的相關檔案發生了改變,那麼就會更新目標檔案,或者目標檔案不存在,那麼這些檔案更新可能會同時發生。
規則檔案的語法如下所示。
target: [ prerequisites ... ] [TAB] command1 [TAB] -command2 # ignore errors [TAB] @command3 # suppress echoing
這裡面的 "[TAB]
" 是一個 TAB 程式碼。每一行在進行變數替換以後會被 shell 解釋。在行末使用
"\
" 來繼續此指令碼。使用 "$$
" 輸入
"$
" 來獲得 shell 指令碼中的環境變數值。
目標跟相關檔案也可以通過隱式規則給出,例如,如下所示。
%.o: %.c header.h
在這裡,目標包含了 "%
" 字元 (只是它們中確切的某一個)。"%
"
字元能夠匹配實際的目標檔案中任意一個非空的子串。相關檔案同樣使用 "%
" 來表明它們是怎樣與目標檔案建立聯絡的。
執行 "make -p -f/dev/null
" 指令來檢視內部自動化的規則。
Autotools 是一套程式設計工具,被設計作為協助將原始碼軟體包移植到許多 類 Unix 系統。
![]() |
警告 |
---|---|
當你安裝編譯好的程式的時候,注意不要覆蓋系統檔案。 |
Debian 不會在 "/usr/local
" 或 "/opt
"
目錄下建立檔案。如果你想要原始碼編譯程式,把它安裝到 "/usr/local/
" 目錄下,因為這並不會影響到
Debian。
$ cd src $ ./configure --prefix=/usr/local $ make # this compiles program $ sudo make install # this installs the files in the system
如果你有原始碼並且它使用
autoconf
(1)/automake
(1),如果你能記得你是怎樣配置它的話,執行如下的指令來解除安裝程式。
$ ./configure all-of-the-options-you-gave-it
$ sudo make uninstall
或者,如果你十分確信安裝程序把檔案都放在了 "/usr/local/
"
下並且這裡沒什麼重要的東西,你可以通過如下的指令來清除它所有的內容。
# find /usr/local -type f -print0 | xargs -0 rm -f
如果你不確定檔案被安裝到了哪裡,你可以考慮使用 checkinstall
軟體包中的
checkinstall
(8),它將會提供一個清晰的解除安裝路徑。現在,它支援建立帶有
“-D
” 選項的 Debian 軟體包。
基本的動態互動網頁可由如下方法制作。
呈現給瀏覽器使用者的是 HTML 形式。
填充並點選表單條目將會從瀏覽器向 web 伺服器傳送帶有編碼參數的下列 URL 字串之一。
"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"
".
Web伺服器上的CGI程式 (任何一個
"program.*
")在執行時,都會使用"$QUERY_STRING
"環境變數.
CGI 程式的 stdout
傳送到瀏覽器,作為互動式的動態 web 頁面展示。
出於安全考慮,最好不要自己從頭編寫解析CGI參數的手藝. 在Perl和Python中有現有的模組可以使用. PHP 中包含這些功能. 當需要客戶端資料儲存時, 可使用HTTP cookies . 當需要處理客戶端資料時, 通常使用Javascript.
更多資訊,參見 通用閘道器介面, Apache 軟體基金會, 和 JavaScript.
直接在瀏覽器地址中輸入 http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial 就可以在 Google 上搜索 “CGI tutorial”。這是在 Google 伺服器上檢視 CGI 指令碼執行的好方法。
如果你想製作一個 Debian 包,閱讀下面內容。
章 2, Debian 軟體包管理 理解基本的包管理系統
節 2.7.13, “移植一個軟體包到 stable 系統” 理解基本的移植過程
節 9.11.4, “Chroot 系統” 理解基本的 chroot 技術
debuild
(1) 和 sbuild
(1)
Debian 維護者指引
(debmake-doc
包)
Debian 開發者參考手冊
(developers-reference
包)
Debian 策略手冊
(debian-policy
包)
debmake
, dh-make
,
dh-make-perl
等軟體包,對軟體包打包過程,也有幫助。