內容目錄
這裡我給出一些 Debian 系統中的資訊,幫助學習程式設計的人找出打包的原始碼。下面是值得關注的軟體包和與之對應的文件。
安裝 manpages
和 manpages-dev
包之後,可以通過運行“man 名稱
”查看手冊頁中的參考資訊。安裝了 GNU
工具的相關文檔包之後,可以通過運行“info 程序名稱
”查看參考文檔。某些 GFDL 協議的文檔與 DFSG
並不兼容,所以你可能需要在 main
倉庫中包含 contrib
和
non-free
才能下載並安裝它們。
請考慮使用版本控制系統工具。參見 節 10.5, “Git”。
警告 | |
---|---|
不要用“ |
注意 | |
---|---|
你可以把從源代碼編譯得到的程序直接放到“ |
提示 | |
---|---|
“歌曲:99瓶啤酒”的代碼示例可以給你提供實踐各種語言的好範本。 |
Shell 腳本 是指包含有下面格式的可執行的文本文件。
#!/bin/sh ... command lines
第一行指明瞭讀取並執行這個文件的 shell 解釋器。
讀懂 shell 指令碼的最好 辦法是先理解類 UNIX 系統是如何工作的。這裡有一些 shell 程式設計的提示。看看“Shell 錯誤”(https://www.greenend.org.uk/rjk/2001/04/shell.html),可以從錯誤中學習。
不像 shell 交互模式(參見節 1.5, “簡單 shell 指令” 和 節 1.6, “類 Unix 的文本處理”),shell 腳本會頻繁使用參數、條件和循環等。
系統中的許多指令碼都可以透過任意 POSIX shell(參見 表格 1.13, “shell 程式列表”)來執行。
預設的非互動 POSIX shell "/usr/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:884, I:997 | 191 | 小和快的 POSIX 相容 shell,用於 sh |
coreutils
|
V:880, I:999 | 18307 | GNU 核心工具 |
grep
|
V:782, I:999 | 1266 | GNU grep 、egrep 和
fgrep |
sed
|
V:790, I:999 | 987 | GNU sed |
mawk
|
V:442, I:997 | 285 | 小和快的 awk |
debianutils
|
V:907, I:999 | 224 | 用於 Debian 的各種工具 |
bsdutils
|
V:519, I:999 | 356 | 來自 4.4BSD-Lite 的基礎工具 |
bsdextrautils
|
V:596, I:713 | 339 | 來自 4.4BSD-Lite 的額外的工具 |
moreutils
|
V:15, I:38 | 231 | 額外的 Unix 工具 |
提示 | |
---|---|
儘管 |
參見 節 1.6, “類 Unix 的文本處理” 的例子。
表格 12.8. 直譯器相關軟體包列表
軟體包 | 流行度 | 大小 | 包 |
---|---|---|---|
dash
|
V:884, I:997 | 191 | sh: 小和快的 POSIX 相容的 shell,用於 sh |
bash
|
V:838, I:999 | 7175 | sh: 由 bash-doc
包提供的“info bash ” |
mawk
|
V:442, I:997 | 285 | AWK: 小和快的 awk |
gawk
|
V:285, I:349 | 2906 | AWK: 由 gawk-doc
包提供的“info gawk ” |
perl
|
V:707, I:989 | 673 | 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 | 171 | Perl 的 REPL : reply (1) |
libdevel-repl-perl
|
V:0, I:0 | 237 | Perl 的 REPL : re.pl (1) |
python3
|
V:718, I:953 | 81 | Python: python3 (1) 以及透過
python3-doc 包提供的 html 文件 |
tcl
|
V:25, I:218 | 21 | Tcl: tcl (3) 以及透過
tcl-doc 包提供的更詳細的手冊頁文件 |
tk
|
V:20, I:211 | 21 | Tk:tk (3) 以及透過
tk-doc 包提供的更詳細的手冊頁文件 |
ruby
|
V:86, I:208 | 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:192, I:566 | 651 | xmessage (1):在一個視窗中顯示一條訊息或疑問(X) |
whiptail
|
V:284, I:996 | 56 | 從 shell 指令碼中顯示使用者友好的對話方塊(newt) |
dialog
|
V:11, I:99 | 1227 | 從 shell 指令碼中顯示使用者友好的對話方塊(ncurses) |
zenity
|
V:76, I:363 | 183 | 從 shell 指令碼中顯示圖形對話方塊(GTK) |
ssft
|
V:0, I:0 | 75 | Shell 指令碼前端工具 (zenity, kdialog, and 帶有 gettext 的 dialog 封裝) |
gettext
|
V:56, I:259 | 5818 | “/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 的強項。與此同時,這允許我們建立令人困惑和繁亂的程式碼。所以請小心。
表格 12.10. 編譯相關軟體包列表
軟體包 | 流行度 | 大小 | 說明 |
---|---|---|---|
gcc
|
V:167, I:550 | 36 | GNU C 編譯器 |
libc6-dev
|
V:248, I:567 | 12053 | GNU C 庫:開發庫和標頭檔案 |
g++
|
V:56, I:501 | 13 | GNU C++ 編譯器 |
libstdc++-10-dev
|
V:14, I:165 | 17537 | GNU 標準 C++ 庫 版本 3(開發檔案) |
cpp
|
V:334, I:727 | 18 | GNU C 預處理 |
gettext
|
V:56, I:259 | 5818 | GNU 國際化工具 |
glade
|
V:0, I:5 | 1204 | GTK 使用者介面構建器 |
valac
|
V:0, I:4 | 725 | 使用 GObject 系統類似 C# 的語言 |
flex
|
V:7, I:73 | 1243 | LEX 相容的 fast lexical analyzer generator |
bison
|
V:7, I:80 | 3116 | YACC 相容的 解析器生成器 |
susv2
|
I:0 | 16 | 通過“單一UNIX規範(版本2)”獲得(英語文檔) |
susv3
|
I:0 | 16 | 通過“單一UNIX規範(版本3)”獲得(英語文檔) |
susv4
|
I:0 | 16 | 透過“單一UNIX規範(版本4)”獲取(英語文件) |
golang
|
I:20 | 11 | Go 程式語言編譯器 |
rustc
|
V:3, I:14 | 8860 | Rust 系統程式語言 |
haskell-platform
|
I:1 | 12 | 標準的 Haskell 庫和工具 |
gfortran
|
V:6, I:62 | 15 | GNU Fortran 95 編譯器 |
fpc
|
I:2 | 103 | 自由 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) 的教程。
很多簡單的例子能夠在
“/usr/share/doc/flex/examples/
”下發現。[7]
在 Debian 裡,有幾個軟體包提供 Yacc相容的前瞻性的 LR 解析 或 LALR 解析的生成器。
可以使用 “info bison
” 檢視 bison
(1) 的教學。
你需要提供你自己的的 "main()
" 和
"yyerror()
".通常,Flex 建立的 "main()
" 呼叫
"yyparse()
",它又呼叫了 "yylex()
".
這裡是一個建立簡單終端計算程式的例子。
讓我們建立 example.y
:
/* calculator source for bison */ %{ #include <stdio.h> extern int yylex(void); extern int yyerror(char *); %} /* declare tokens */ %token NUMBER %token OP_ADD OP_SUB OP_MUL OP_RGT OP_LFT OP_EQU %% calc: | calc exp OP_EQU { printf("Y: RESULT = %d\n", $2); } ; exp: factor | exp OP_ADD factor { $$ = $1 + $3; } | exp OP_SUB factor { $$ = $1 - $3; } ; factor: term | factor OP_MUL term { $$ = $1 * $3; } ; term: NUMBER | OP_LFT exp OP_RGT { $$ = $2; } ; %% int main(int argc, char **argv) { yyparse(); } int yyerror(char *s) { fprintf(stderr, "error: '%s'\n", s); }
讓我們建立 example.l
:
/* calculator source for flex */ %{ #include "example.tab.h" %} %% [0-9]+ { printf("L: NUMBER = %s\n", yytext); yylval = atoi(yytext); return NUMBER; } "+" { printf("L: OP_ADD\n"); return OP_ADD; } "-" { printf("L: OP_SUB\n"); return OP_SUB; } "*" { printf("L: OP_MUL\n"); return OP_MUL; } "(" { printf("L: OP_LFT\n"); return OP_LFT; } ")" { printf("L: OP_RGT\n"); return OP_RGT; } "=" { printf("L: OP_EQU\n"); return OP_EQU; } "exit" { printf("L: exit\n"); return YYEOF; } /* YYEOF = 0 */ . { /* ignore all other */ } %%
按下面的方法來從 shell 提示符執行來嘗試這個:
$ bison -d example.y $ flex example.l $ gcc -lfl example.tab.c lex.yy.c -o example $ ./example 1 + 2 * ( 3 + 1 ) = L: NUMBER = 1 L: OP_ADD L: NUMBER = 2 L: OP_MUL L: OP_LFT L: NUMBER = 3 L: OP_ADD L: NUMBER = 1 L: OP_RGT L: OP_EQU Y: RESULT = 9 exit L: exit
類似 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 | 147 | Emacs Lisp 正則錯誤發現器 |
cppcheck-gui
|
V:0, I:1 | 7224 | 靜態 C/C++ 程式碼分析工具(GUI) |
shellcheck
|
V:2, I:13 | 18987 | shell 指令碼的 lint 工具 |
pyflakes3
|
V:2, I:15 | 20 | Python 3 程式被動檢查器 |
pylint
|
V:4, I:20 | 2018 | Python 程式碼靜態檢查器 |
perl
|
V:707, I:989 | 673 | 帶有內部靜態程式碼檢測的直譯器:B::Lint (3perl) |
rubocop
|
V:0, I:0 | 3247 | Ruby 靜態程式碼分析器 |
clang-tidy
|
V:2, I:11 | 21 | 基於 clang 的 C++ 規則格式檢查工具 |
splint
|
V:0, I:2 | 2320 | 靜態檢查 C 程式 bug 的工具 |
flawfinder
|
V:0, I:0 | 205 | 檢查 C/C++ 原始碼和查詢安全漏洞的工具 |
black
|
V:3, I:13 | 660 | 強硬的 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 | 1908 | 原始碼檢索和瀏覽工具 |
exuberant-ctags
|
V:2, I:20 | 341 | 構建原始碼定義的標籤檔案索引 |
universal-ctags
|
V:1, I:11 | 3386 | 構建原始碼定義的標籤檔案索引 |
除錯是程式中很重要的一部分。知道怎樣去除錯程式,能夠讓你成為一個好的 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 /usr/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:248, I:567 | 12053 | mtrace (1):除錯 glibc 中的 malloc |
valgrind
|
V:6, I:37 | 78191 | 記憶體偵錯程式和分析器 |
electric-fence
|
V:0, I:3 | 73 | malloc (e) 偵錯程式 |
libdmalloc5
|
V:0, I:2 | 390 | 記憶體分配庫除錯 |
duma
|
V:0, I:0 | 296 | 在 C 和 C++ 程式中檢測快取溢位和快取欠載( buffer under-runs )的庫 |
leaktracer
|
V:0, I:1 | 56 | C++ 程式記憶體洩露跟蹤器 |
表格 12.16. 編譯工具軟體包列表
軟體包 | 流行度 | 大小 | 包 |
---|---|---|---|
make
|
V:151, I:555 | 1592 | 通過 make-doc 包提供的“info make ” |
autoconf
|
V:31, I:230 | 2025 | 由 autoconf-doc 包提供的“info autoconf ” |
automake
|
V:30, I:228 | 1837 | 由 automake1.10-doc 包提供的“info automake ” |
libtool
|
V:25, I:212 | 1213 | 由 libtool-doc 包提供"info libtool " |
cmake
|
V:17, I:115 | 36607 | cmake (1) 跨平臺、開源的編譯系統 |
ninja-build
|
V:6, I:41 | 428 | ninja (1) 接近 Make 精髓的小編譯系統 |
meson
|
V:3, I:22 | 3759 | meson (1) 在 ninja 之上的高生產力的構建系統 |
xutils-dev
|
V:0, I:9 | 1484 | 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 字串之一。
"https://www.foo.dom/cgi-bin/program.pl?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3
"
"https://www.foo.dom/cgi-bin/program.py?VAR1=VAL1&VAR2=VAL2&VAR3=VAL3
"
"https://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.
直接在瀏覽器地址中輸入 https://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
等軟體包,對軟體包打包過程,也有幫助。