內容目錄
這裡我給出一些 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:879, I:996 | 191 | 小和快的 POSIX 相容 shell,用於 sh |
coreutils
|
V:876, I:999 | 18062 | GNU 核心工具 |
grep
|
V:780, I:999 | 1267 | GNU grep 、egrep 和
fgrep |
sed
|
V:781, I:999 | 987 | GNU sed |
mawk
|
V:414, I:997 | 284 | 小和快的 awk |
debianutils
|
V:903, I:999 | 244 | 用於 Debian 的各種工具 |
bsdutils
|
V:560, I:999 | 356 | 來自 4.4BSD-Lite 的基礎工具 |
bsdextrautils
|
V:532, I:648 | 339 | 來自 4.4BSD-Lite 的額外的工具 |
moreutils
|
V:13, I:36 | 244 | 額外的 Unix 工具 |
![]() |
提示 |
---|---|
儘管 |
參見 節 1.6, “類 Unix 的文本處理” 的例子。
表格 12.8. 直譯器相關軟體包列表
軟體包 | 流行度 | 大小 | 包 |
---|---|---|---|
dash
|
V:879, I:996 | 191 | sh: 小和快的 POSIX 相容的 shell,用於 sh |
bash
|
V:823, I:999 | 7168 | sh: 由 bash-doc
包提供的“info bash ” |
mawk
|
V:414, I:997 | 284 | AWK: 小和快的 awk |
gawk
|
V:290, I:363 | 2906 | AWK: 由 gawk-doc
包提供的“info gawk ” |
perl
|
V:648, I:990 | 669 | Perl: perl (1) 以及透過
perl-doc 和 perl-doc-html 提供的 html 文件 |
libterm-readline-gnu-perl
|
V:2, I:28 | 379 | GNU ReadLine/History 庫的 Perl 擴充套件: perlsh (1) |
libreply-perl
|
V:0, I:0 | 171 | Perl 的 REPL : reply (1) |
libdevel-repl-perl
|
V:0, I:0 | 237 | Perl 的 REPL : re.pl (1) |
python3
|
V:699, I:944 | 82 | Python: python3 (1) 以及透過
python3-doc 包提供的 html 文件 |
tcl
|
V:25, I:233 | 20 | Tcl: tcl (3) 以及透過
tcl-doc 包提供的更詳細的手冊頁文件 |
tk
|
V:20, I:226 | 20 | Tk:tk (3) 以及透過
tk-doc 包提供的更詳細的手冊頁文件 |
ruby
|
V:77, I:223 | 29 | 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:178, I:565 | 712 | xmessage (1):在一個視窗中顯示一條訊息或疑問(X) |
whiptail
|
V:265, I:996 | 57 | 從 shell 指令碼中顯示使用者友好的對話方塊(newt) |
dialog
|
V:11, I:103 | 1213 | 從 shell 指令碼中顯示使用者友好的對話方塊(ncurses) |
zenity
|
V:74, I:358 | 155 | 從 shell 指令碼中顯示圖形對話方塊(GTK) |
ssft
|
V:0, I:0 | 75 | Shell 指令碼前端工具 (zenity, kdialog, and 帶有 gettext 的 dialog 封裝) |
gettext
|
V:54, I:264 | 5825 | “/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:161, I:552 | 47 | GNU C 編譯器 |
libc6-dev
|
V:247, I:569 | 12048 | GNU C 庫:開發庫和標頭檔案 |
g++
|
V:51, I:498 | 14 | GNU C++ 編譯器 |
libstdc++-10-dev
|
V:19, I:212 | 17537 | GNU 標準 C++ 庫 版本 3(開發檔案) |
cpp
|
V:325, I:729 | 30 | GNU C 預處理 |
gettext
|
V:54, I:264 | 5825 | GNU 國際化工具 |
glade
|
V:0, I:5 | 1209 | GTK 使用者介面構建器 |
valac
|
V:0, I:5 | 724 | 使用 GObject 系統類似 C# 的語言 |
flex
|
V:7, I:73 | 1241 | LEX 相容的 fast lexical analyzer generator |
bison
|
V:7, I:80 | 3116 | YACC 相容的 解析器生成器 |
susv2
|
I:0 | 16 | 通過“單一UNIX規範(版本2)”獲得(英語文檔) |
susv3
|
I:0 | 16 | 通過“單一UNIX規範(版本3)”獲得(英語文檔) |
golang
|
I:18 | 11 | Go 程式語言編譯器 |
rustc
|
V:3, I:14 | 9490 | Rust 系統程式語言 |
haskell-platform
|
I:1 | 12 | 標準的 Haskell 庫和工具 |
gfortran
|
V:6, I:64 | 16 | GNU Fortran 95 編譯器 |
fpc
|
I:2 | 102 | 自由 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 | 2591 | 用於 Vim 8 和 NeoVim 的非同步 Lint 引擎 |
vim-syntastic
|
I:3 | 1379 | vim 語法檢查利器 |
elpa-flycheck
|
V:0, I:1 | 808 | Emacs 現代實時語法檢查 |
elpa-relint
|
V:0, I:0 | 144 | Emacs Lisp 正則錯誤發現器 |
cppcheck-gui
|
V:0, I:1 | 6508 | 靜態 C/C++ 程式碼分析工具(GUI) |
shellcheck
|
V:2, I:11 | 18987 | shell 指令碼的 lint 工具 |
pyflakes3
|
V:1, I:13 | 24 | Python 3 程式被動檢查器 |
pylint
|
V:3, I:17 | 2047 | Python 程式碼靜態檢查器 |
perl
|
V:648, I:990 | 669 | 帶有內部靜態程式碼檢測的直譯器:B::Lint (3perl) |
rubocop
|
V:0, I:0 | 3247 | Ruby 靜態程式碼分析器 |
clang-tidy
|
V:1, I:9 | 21 | 基於 clang 的 C++ 規則格式檢查工具 |
splint
|
V:0, I:1 | 2320 | 靜態檢查 C 程式 bug 的工具 |
flawfinder
|
V:0, I:0 | 205 | 檢查 C/C++ 原始碼和查詢安全漏洞的工具 |
black
|
V:2, I:10 | 601 | 強硬的 Python 程式碼格式化器 |
perltidy
|
V:0, I:4 | 2493 | Perl 指令碼縮排和重新格式化 |
indent
|
V:0, I:7 | 431 | C 語言原始碼格式化程式 |
astyle
|
V:0, I:2 | 785 | C、 C++、 Objective-C、 C# 和 Java 的原始碼縮排器 |
bcpp
|
V:0, I:0 | 111 | 美化 C(++) |
xmlindent
|
V:0, I:1 | 53 | XML 流 重新格式化 |
global
|
V:0, I:2 | 1895 | 原始碼檢索和瀏覽工具 |
exuberant-ctags
|
V:2, I:20 | 341 | 構建原始碼定義的標籤檔案索引 |
除錯是程式中很重要的一部分。知道怎樣去除錯程式,能夠讓你成為一個好的 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:247, I:569 | 12048 | mtrace (1):除錯 glibc 中的 malloc |
valgrind
|
V:5, I:33 | 77683 | 記憶體偵錯程式和分析器 |
electric-fence
|
V:0, I:2 | 73 | malloc (e) 偵錯程式 |
libdmalloc5
|
V:0, I:0 | 393 | 記憶體分配庫除錯 |
duma
|
V:0, I:0 | 293 | 在 C 和 C++ 程式中檢測快取溢位和快取欠載( buffer under-runs )的庫 |
leaktracer
|
V:0, I:0 | 56 | C++ 程式記憶體洩露跟蹤器 |
表格 12.16. 編譯工具軟體包列表
軟體包 | 流行度 | 大小 | 包 |
---|---|---|---|
make
|
V:151, I:559 | 1592 | 通過 make-doc 包提供的“info make ” |
autoconf
|
V:30, I:236 | 2025 | 由 autoconf-doc 包提供的“info autoconf ” |
automake
|
V:29, I:235 | 1837 | 由 automake1.10-doc 包提供的“info automake ” |
libtool
|
V:25, I:220 | 1213 | 由 libtool-doc 包提供"info libtool " |
cmake
|
V:14, I:111 | 35698 | cmake (1) 跨平臺、開源的編譯系統 |
ninja-build
|
V:5, I:37 | 428 | ninja (1) 接近 Make 精髓的小編譯系統 |
meson
|
V:2, I:21 | 3685 | meson (1) 在 ninja 之上的高生產力的構建系統 |
xutils-dev
|
V:0, I:9 | 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 軟體包。
軟體構建系統也在演進:
CMake initially released in 2000 improved speed significantly but was originally built on the top of inherently slow Make. (Now Ninja can be its backend.)
Ninja initially released in 2012 is meant to replace Make for the further improved build speed and is designed to have its input files generated by a higher-level build system.
參見在 "The Meson Build system" 和 "The Ninja build system"裡發現的文件。
基本的動態互動網頁可由如下方法制作。
呈現給瀏覽器使用者的是 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
等軟體包,對軟體包打包過程,也有幫助。