第12章 プログラミング

目次

12.1. シェルスクリプト
12.1.1. POSIX シェル互換性
12.1.2. シェル変数
12.1.3. シェル条件式
12.1.4. シェルループ
12.1.5. シェルコマンドライン処理シーケンス
12.1.6. シェルスクリプトのためのユーティリティープログラム
12.1.7. シェルスクリプトダイアログ
12.1.8. zenity を使うシェルスクリプト例
12.2. Make
12.3. C
12.3.1. 単純な C プログラム (gcc)
12.4. デバグ
12.4.1. 基本的な gdb 実行
12.4.2. Debian パッケージのデバグ
12.4.3. バックトレースの収集
12.4.4. 上級 gdb コマンド
12.4.5. X エラーのデバグ
12.4.6. ライブラリーへの依存の確認
12.4.7. メモリーリーク検出ツール
12.4.8. 静的コード分析ツール
12.4.9. バイナリーのディスアッセンブリー
12.5. Flex — 改良版 Lex
12.6. Bison — 改良版 Yacc
12.7. Autoconf
12.7.1. プログラムをコンパイルとインストール
12.7.2. プログラムのアンインストール
12.8. 究極の短い Perl スクリプト
12.9. ウェッブ
12.10. ソースコード変換
12.11. Debian パッケージ作成

パッケージされたソースコードを追跡するのに十分な範囲で Debian システム上でプログラムを学ぶ人への指針を示します。次はプログラムの関して特記すべきパッケージと対応する文書パッケージです。

表12.1 プログラムをすることを補助するパッケージのリスト

パッケージ ポプコン サイズ 文書
autoconf V:3, I:24 2268 autoconf-doc が提供する "info autoconf
automake V:3, I:21 1509 automake1.10-doc が提供する "info automake"
bash V:90, I:99 3935 bash-doc が提供する "info bash"
bison V:2, I:14 1518 bison-doc が提供する "info bison"
cpp V:41, I:83 63 cpp-doc が提供する "info cpp"
ddd V:0.3, I:2 3636 ddd-doc が提供する "info ddd"
exuberant-ctags V:1.0, I:4 284 exuberant-ctags(1)
flex V:2, I:14 1352 flex-doc が提供する "info flex"
gawk V:28, I:32 1932 gawk-doc が提供する "info gawk"
gcc V:20, I:68 41 gcc-doc が提供する "info gcc"
gdb V:3, I:19 6507 gdb-doc が提供する "info gdb"
gettext V:7, I:42 6643 gettext-doc が提供する "info gettext"
gfortran V:1.1, I:6 33 gfortran-doc が提供する "info gfortran" (Fortran 95)
fpc I:0.5 36 python-doc が提供する python(1) と html ページ (Pascal)
glade V:0.2, I:2 1751 メニューが提供するヘルプ (UI Builder)
libc6 V:95, I:99 9497 glibc-docglibc-doc-reference が提供する "info libc"
make V:21, I:70 1220 make-doc が提供する "info make"
xutils-dev V:0.8, I:7 1421 imake(1), xmkmf(1), 他
mawk V:65, I:99 198 mawk(1)
perl V:79, I:99 17076 perl-docperl-doc-html が提供する perl(1) と html
python V:68, I:97 655 python-doc が提供する python(1) と html ページ
tcl8.4 V:8, I:46 3332 tcl8.4-doc が提供する tcl(3) と詳細なマンページ
tk8.4 V:3, I:23 2712 tk8.4-doc が提供する tk(3) と詳細なマンページ
ruby V:6, I:23 36 ri が提供する ruby(1) と詳細なマンページ
vim V:17, I:38 1873 vim-doc が提供するヘルプ (F1) メニュー
susv2 I:0.03 48 "The Single Unix Specifications v2" を取得
susv3 I:0.07 48 "The Single Unix Specifications v3" を取得

オンラインリファレンスは manpagesmanpages-dev パッケージをインストールした後で "man name" とタイプすると利用可能です。GNU ツールのオンラインリファレンスは該当する文書パッケージをインストールした後で "info program_name" とタイプすると使えます。一部の GFDL 文書は DFSG に準拠していないと考えられているので main アーカイブに加えて contribnon-free アーカイブを含める必要があるかもしれません。

[警告] 警告

"test" を実行可能なテストファイルの名前に用いてはいけません。"test" はシェルのビルトインです。

[注意] 注意

ソースから直接コンパイルしたソフトウエアープログラムは、システムプログラムとかち合わないように、"/usr/local" か "/opt" の中にインストールします。

[ヒント] ヒント

"99ボトルのビールの歌" 作成のコード例はほとんど全てのプログラム言語に関する理解のための非常に好適です。

12.1. シェルスクリプト

シェルスクリプトは実行ビットがセットされたテキストファイルで、以下に示すフォーマットのコマンドを含んでいます。

#!/bin/sh
 ... コマンド行

最初の行はこのファイル内容を読み実行するシェルインタープリタを指定します。

シェルスクリプトを読むのは Unix 的なシステムがどのように機能しているのかを理解する最良の方法です。ここでは、シェルプログラムに関する指針や心がけを記します。失敗から学ぶために "シェルの失敗" (http://www.greenend.org.uk/rjk/2001/04/shell.html) を参照下さい。

シェル対話モード (「シェルプロンプト」「Unix 的テキスト処理」参照下さい) と異なり、シェルスクリプトは変数や条件文やループを繁用します。

12.1.1. POSIX シェル互換性

多くのシステムスクリプトは POSIX シェル (表1.13「シェルプログラムのリスト」参照下さい) のどれで解釈されるか分かりません。システムのデフォールトシェルは実際のプログラムをさしているシムリンクである "/bin/sh" です。

  • bash(1)lenny 以前の場合

  • dash(1)squeeze 以降の場合

全ての POSIX シェル間でポータブルとするために bashismszshisms を使うシェルスクリプトを書くのを避けましょう。checkbashisms(1) を使うとこれがチェックできます。

表12.2 典型的 bashizms のリスト

推薦: 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() { … }
8進表記: "\377" 16進表記: "\xff"

"echo" コマンドはその実装がシェルビルトインや外部コマンド間で相違しているので次の注意点を守って使わなければいけません。

  • 特に "-e" と "-E" のコマンドオプション使用を回避します。

  • "-n" 以外のどのコマンドオプション使用をも回避します。

  • 文字列中にエスケープシーケンスはその取扱いに相違があるので使用を避けます。

[注記] 注記

"-n" オプションは実は POSIX シンタックスではありませんが、一般的に許容されています。

[ヒント] ヒント

出力文字列にエスケープシーケンスを埋め込む必要がある場合には、"echo" コマンドの代わりに "printf" コマンドを使います。

12.1.2. シェル変数

特別なシェルパラメーターがシェルスクリプト中ではよく使われます。

表12.3 シェル変数のリスト

シェル変数 変数値
$0 シェルまたはシェルスクリプトの名前
$1 最初 (1番目) のシェル引数
$9 9番目のシェル引数
$# シェル引数の数
"$*" "$1 $2 $3 $4 … "
"$@" "$1" "$2" "$3" "$4" …
$? 最近実行のコマンドの終了状態
$$ このシェルスクリプトの PID
$! 最近スタートしたバックグラウンドジョブの PID

覚えておくべき基本的なパラメーター展開を次に記します。

表12.4 シェル変数展開のリスト

パラメーター式形 var が設定されていればの値 var が設定されていなければの値
${var:-string} "$var" "string"
${var:+string} "string" "null"
${var:=string} "$var" "string" (合わせて "var=string" を実行)
${var:?string} "$var" "string" をstderr に出力 (エラーとともに exit する)

ここで、これら全てのオペレーターのコロン ":" は実際はオプションです。

  • ":" 付き = 存在非ヌル文字列をテストするオペレータ

  • ":" 無し = 存在のみをテストするオペレータ

表12.5 重要なシェル変数置換のリスト

パラメーター置換形 結果
${var%suffix} 最短のサフィクスパターンを削除
${var%%suffix} 最長のサフィクスパターンを削除
${var#prefix} 最短のプリフィクスパターンを削除
${var##prefix} 最長のプリフィクスパターンを削除

12.1.3. シェル条件式

各コマンドは条件式に使えるエグジットステイタスを返します。

  • 成功: 0 ("真")

  • エラー: 非0 ("偽")

[注記] 注記

シェル条件文の文脈中の "0" は "真" を意味します、一方 C 条件文の文脈中の "0" は "偽" を意味します。

[注記] 注記

"[" は、"]" までの引数を条件式として評価する、test コマンドと等価です。

覚えておくべき基本的な条件文の慣用句は次です。

  • "<command> && <成功したらこのcommandも実行> || true"

  • "<command> || <もしcommandが成功しないとこのコマンドも実行> || true"

  • 次のようなマルチラインスクリプト断片

if [ <条件式> ]; then
 <成功ならこのコマンドを実行>
else
 <成功でばいならこのコマンドを実行>
fi

ここで、シェルスクリプトが "-e" フラグ付きで起動された際にシェルスクリプトがこの行で exit しないようにするために末尾の "|| true" が必要です。

表12.6 条件式中のファイル比較オペレーター

論理真を返す条件
-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.7 条件式中での文字列比較オペレータのリスト

論理真を返す条件
-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" です。

12.1.4. シェルループ

POSIX シェル中で使われるループの慣用句があります。

  • "for x in foo1 foo2 … ; do コマンド ; done" は"foo1 foo2 …" リストの項目を変数 "x" にアサインし "コマンド" を実行してループします。

  • "while 条件 ; do コマンド ; done" は"条件" が真の場合 "コマンド" を繰り返します。

  • "until 条件 ; do コマンド ; done" は"条件" が真でない場合 "コマンド" を繰り返します。

  • "break" に出会うと、ループからの脱出が出来ます。

  • "continue" に出会うと、次のループ初めに戻りループを再開します。

[ヒント] ヒント

C 言語のような数字の繰り返しは "foo1 foo2 ..." 生成に seq(1) 使って実現します。

12.1.5. シェルコマンドライン処理シーケンス

シェルはおおよそ次のシーケンスでスクリプトを処理します。

  • シェルは1行読み込みます。

  • シェルは、もし "…"'…' の中なら、行の一部を1つのトークンとしてグループします。

  • シェルは1行を次のによってトークンに分割します。

    • 空白: <space> <tab> <newline>

    • メタ文字: < > | ; & ( )

  • シェルは、もし "…"'…' の中でないなら、各トークンを予約語に対してチェックしその挙動を調整します。

    • 予約語: if then elif else fi for in while unless do done case esac

  • シェルは、もし "…"'…' の中でないなら、エリアスを展開します。

  • シェルは、もし"…"'…'の中でないなら、ティルドを展開します。

    • "~" → 現ユーザーのホームディレクトリー

    • "~<user>" → <user> のホームディレクトリー

  • シェルは、もし '…' の中でないなら、パラメーター"をその値に展開します。

    • パラメーター: "$PARAMETER" or "${PARAMETER}"

  • シェルは、もし '…' の中でないなら、コマンドの置き換えを展開します。

    • "$( command )" → "command" の出力

    • "` command `" → "command" の出力

  • シェルは、もし "…"'…' の中でないなら、パス名のグロブを展開します。

    • * → いかなる文字

    • ? → 1文字

    • […] → "" 中の1つ

  • シェルはコマンドを次から検索して実行します。

    • 関数定義

    • ビルトインコマンド

    • "$PATH" 中の実行ファイル

  • シェルは次行に進みこのプロセスを一番上から順に反復します。

ダブルクォートの中のシングルクォートは特段の効果はありません。

シェル環境中で "set -x" を実行したり、シェルを "-x" オプションで起動すると、シェルは実行するコマンドを全てプリントするようになります。これはデバグをするのに非常に便利です。

12.1.6. シェルスクリプトのためのユーティリティープログラム

Debian システム上でできるだけポータブルなシェルプログラムとするには、ユーティリティープログラムを essential パッケージで提供されるプログラムだけに制約するのが賢明です。

  • "aptitude search ~E" はessential (必須) パッケージをリストします。

  • "dpkg -L <パッケージ名> |grep '/man/man.*/'" は <パッケージ名> パッケージによって提供されるコマンドのマンページをリストします。

表12.8 シェルスクリプト用の小さなユーティリティープログラムを含むパッケージのリスト

パッケージ ポプコン サイズ 説明
coreutils V:90, I:99 13855 GNU コアユーティリティー
debianutils V:94, I:99 222 Debian 限定の雑ユーティリティー
bsdmainutils V:85, I:99 558 FreeBSD 由来の追加ユーティリティー集
bsdutils V:81, I:99 187 4.4BSD-Lite 由来の基本ユーティリティー
moreutils V:0.4, I:1.8 220 追加の Unix ユーティリティー

[ヒント] ヒント

moreutils は Debian の外では存在しないかも知れませんが、興味深い小さなプログラムを提供します。もっとも特記すべきは、オリジナルファイルを上書きしたいときに非常に有効な sponge(8) です。

12.1.7. シェルスクリプトダイアログ

簡単なシェルプログラムのユーザーインターフェースは、echoread コマンドを使った退屈な相互作用からいわゆる対話 (dialog) プログラム等の一つを使うことでよりよい相互作用になります。

表12.9 ユーザーインターフェースプログラムのリスト

パッケージ ポプコン サイズ 説明
x11-utils V:28, I:59 668 xmessage(1): window 中にメッセージや質問を表示 (X)
whiptail V:42, I:99 87 シェルスクリプトからユーザーフレンリーなダイアログボックスを表示 (newt)
dialog V:2, I:16 1230 シェルスクリプトからユーザーフレンリーなダイアログボックスを表示 (ncurses)
zenity V:12, I:45 316 シェルスクリプトからグラフィカルなダイアログボックスを表示 (gtk2.0)
ssft V:0.01, I:0.11 152 シェルスクリプトフロントエンドツール (gettext を使った zenity や kdialog や dialog のラッパー)
gettext V:7, I:42 6643 "/usr/bin/gettext.sh": メッセージ翻訳

12.1.8. zenity を使うシェルスクリプト例

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" のようなコマンド設定をしたローンチャを作るのも面白いかもしれません。

12.2. Make

Make はプログラムのグループを管理するためのユーティリティーです。make(1) を実行すると、make は"Makefile" というルールファイルを読み、ターゲットが最後に変更された後で変更された前提ファイルにターゲットが依存している場合やターゲットが存在しない場合にはターゲットを更新します。このような更新は同時並行的にされるかもしれません。

ルールファイルのシンタックスは次です。

ターゲット: [ 前提 ... ]
 [TAB]  command1
 [TAB]  -command2 # エラー無視
 [TAB]  @command3 # エコー抑制

上記で、" [TAB] " は TAB コードです。各行は make による変数置換後シェルによって解釈されます。スクリプトを継続する行末には "\" を使います。シェルスクリプトの環境変数のための "$" を入力するためには "$$" を使います。

ターゲットや前提に関するインプリシット (暗黙) ルールは、例えば次のように書けます。

%.o: %.c header.h

上記で、ターゲットは "%" という文字を (1つだけ) 含んでいます。"%" は実際のターゲットファイル名の空でないいかなる部分文字列ともマッチします。前提もまた同様にそれらの名前が実際のターゲットファイル名にどう関連するかを示すために "%" を用いることができます。

表12.10 make の自動変数のリスト

自動変数 変数値
$@ ターゲット
$< 最初の前提条件
$? 全ての新規の前提条件
$^ 全ての前提条件
$* "%" はターゲットパターンの軸にマッチします

表12.11 make 変数の展開のリスト

変数展開 説明
foo1 := bar 一回だけの展開
foo2 = bar 再帰的展開
foo3 += bar 後ろに追加

"make -p -f/dev/null" を実行して自動的な内部ルールを確認下さい。

12.3. C

C プログラム言語で書かれたプログラムをコンパイルする適正な環境を次のようにして設定できます。

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

GNU C ライブラリーパッケージである libc6-dev パッケージは、C プログラム言語で使われるヘッダーファイルやライブラリールーチンの集合である C 標準ライブラリーを提供します。

C のリファレンスは以下を参照下さい。

  • "info libc" (C ライブラリー関数リファレンス)

  • gcc(1) と "info gcc"

  • 各 C ライブラリー関数名(3)

  • Kernighan & Ritchie 著, "The C Programming Language", 第2版 (Prentice Hall)

12.3.1. 単純な C プログラム (gcc)

簡単な例の "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

ここで、"-lm" はsqrt(3) のために libc6 パッケージで提供されるライブラリー "/usr/lib/libm.so" をリンクするのに必要です。実際のライブラリーは "/lib/" 中にあるファイル名 "libm.so.6" で、それは "libm-2.7.so" にシムリンクされています。

出力テキスト中の最後のパラメーターを良く見ましょう。"%10s" が指定されているにもかかわらず10文字以上あります。

上記のオーバーラン効果を悪用するバッファーオーバーフロー攻撃を防止のために、sprintf(3)strcpy(3) 等の境界チェック無しのポインターメモリー操作関数の使用は推奨できません。これに代えて snprintf(3)strncpy(3) を使います。

12.4. デバグ

デバグは重要なプログラム活動です。プログラムのデバグをどうしてするかを知っていることで、あなたも意味あるバグリポートを作成できる良い Debian ユーザーになれます。

12.4.1. 基本的な gdb 実行

Debian 上の第一義的デバッガは、実行中のプログラムを検査できるようにする gdb(1) です。

gdb と関連プログラムを次のようにインストールしましょう。

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

gdb の良い入門書は "info gdb" とかネット上に色々あります。次は gdb(1) を"-g" を使ってデバグ情報を付けてコンパイルされた "program" に使う簡単な例です。

$ 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
[ヒント] ヒント

多くの gdb(1) コマンドは省略できます。タブ展開はシェル同様に機能します。

12.4.2. Debian パッケージのデバグ

Debian システムではデフォールトではインストールされたバイナリーはストリップされているべきなので、通常のパッケージではほとんどのデバグシンボルが削除されています。gdb(1) を使って Debian パッケージをデバグするには、対応する *-dbg パッケージをインストールする必要があります (例えば libc6 の場合 libc6-dbg)。

デバグしようとしているパッケージに *-dbg パッケージが無い場合は、次のようにしてリビルドした後でインストールする必要があります。

$ mkdir /path/new ; cd /path/new
$ sudo apt-get update
$ sudo apt-get dist-upgrade
$ sudo apt-get install fakeroot devscripts build-essential
$ sudo apt-get build-dep source_package_name
$ apt-get source package_name
$ cd package_name*

必要に応じてバグを修正します。

例えば次のように、既存パッケージを再コンパイルする時は "+debug1" を後ろに付けたり、リリース前のパッケージをコンパイルする時は "~pre1" を後ろに付けたりと、正規の Debian バージョンとかち合わないようにパッケージバージョンを増やします。

$ dch -i

次のようにしてデバグシンボル付きでパッケージをコンパイルしてインストールします。

$ export DEB_BUILD_OPTIONS=nostrip,noopt
$ debuild
$ cd ..
$ sudo debi package_name*.changes

パッケージのビルドスクリプトを確認して、バイナリーのコンパイルに確実に "CFLAGS=-g -Wall" が使われているようにします。

12.4.3. バックトレースの収集

プログラムがクラッシュするのに出会った場合に、バックトレース情報をバグレポートに切り貼りして報告するのは良い考えです。

バックトレースは次のような段取りで得られます。

  • gdb(1) の下でプログラム実行します。

  • クラッシュを再現します。

    • gdb プロンプトに落ちて戻るようになります。

  • gdb プロンプトで "bt" とタイプします。

プログラムがフリーズした場合には、gdb を実行しているターミナルで Ctrl-C を押すことでプログラムをクラッシュさせて gdb プロンプトが得られます。

[ヒント] ヒント

しばしば、一番上数行が "malloc()" か "g_malloc()" 中にあるバックトレースを見かけます。こういったことが起こる場合は、大体あまりあなたのバックトレースは役に立ちません。有用な情報を見つけるもっとも簡単な方法は環境変数 "$MALLOC_CHECK_" の値を 2と設定することです (malloc(3))。gdb を実行しながらこれを実行するには次のようにします。

 $ MALLOC_CHECK_=2 gdb hello

12.4.4. 上級 gdb コマンド

表12.12 上級 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")

12.4.5. X エラーのデバグ

GNOME プログラム preview1 が X エラーを受けると、次のようなメッセージが見つかります。

The program 'preview1' received an X Window System error.

このような場合には、プログラムを "--sync" 付きで実行して、バックトレースを得るために "gdk_x_error" 関数上で停止するようにしてみましょう。

12.4.6. ライブラリーへの依存の確認

次のように 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)

`chroot` された環境下で ls(1) が機能するには、上記ライブラリーがあなたの `chroot` された環境内で利用可能である必要があります。

「プログラム活動の追跡」を参照下さい。

12.4.7. メモリーリーク検出ツール

Debian にはメモリーリークを検出するプログラムがいくつか利用可能です。

表12.13 メモリーリーク検出ツールのリスト

パッケージ ポプコン サイズ 説明
libc6-dev V:49, I:68 10553 mtrace(1): glibc 中の malloc デバッグ機能
valgrind V:1.1, I:7 72967 メモリーデバッガとプロファイラ
kmtrace V:0.3, I:3 323 glibc のmtrace(1) を使う KDE メモリーリークトレースソフト
alleyoop V:0.04, I:0.3 948 メモリーチェックソフトの GNOME フロントエンド
electric-fence V:0.03, I:0.7 46 malloc(3) デバッガ
leaktracer V:0.00, I:0.10 116 C++ プログラム用のメモリーリーク追跡ソフト
libdmalloc5 V:0.01, I:0.17 324 メモリーアロケーションのデバグ用ライブラリー

12.4.8. 静的コード分析ツール

静的コード分析用の lint のようなツールがあります。

表12.14 静的コード分析ツールのリスト

パッケージ ポプコン サイズ 説明
splint V:0.07, I:0.4 1836 C プログラムを静的にバグのチェックするためのツール
rats V:0.04, I:0.19 876 セキュリティーの大まかな監査ツール (C, C++, PHP, Perl, Python コード)
flawfinder V:0.02, I:0.14 188 C/C++ ソースコードを検査してセキュリティーの脆弱性を探すツール
perl V:79, I:99 17076 静的コードチェックソフト付きのインタープリタ: B::Lint(3perl)
pylint V:0.2, I:1.0 416 Python コード静的チェックソフト
jlint V:0.01, I:0.06 156 Java プログラム静的チェックソフト
weblint-perl V:0.07, I:0.6 58 HTML 用のシンタックス最小限の文体チェックソフト
linklint V:0.04, I:0.2 432 高速リンクチェックソフトとウェッブサイトメンテツール
libxml2-utils V:4, I:49 138 XML ファイルを検証する xmllint(1) を含むユーティリティー

12.4.9. バイナリーのディスアッセンブリー

次のように objdump(1) を使ってバイナリーコードをディスアッセンブルできます。

$  objdump -m i386 -b binary -D /usr/lib/grub/x86_64-pc/stage1
[注記] 注記

gdb(1) は対話的にコードをディスアッセンブルするのに使えます。

12.5. Flex — 改良版 Lex

FlexLex 互換の高速字句解析生成ソフトです。

flex(1) の入門書は "info flex" の中にあります。

自分で作った "main()" と "yywrap()" を供給する必要があります。そうでない場合にはあなたの flex プログラムは次のようでなければライブラリー無しにコンパイル出来ません。これというのは "yywrap" はマクロで、"%option main" とすると "%option noyywrap" が暗示的に有効になるからです。

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

上記の代わりにとして、cc(1) のコマンドラインの最後に (ちょうど AT&T-Lex が"-ll" 付きであるように) "-lfl" リンカーオプションを使いコンパイルすることが出来ます。この場合、"%option" は必要なくなります。

12.6. Bison — 改良版 Yacc

Yacc 互換の前方参照可能な LR パーサーとか LALR パーサー生成ソフトは、いくつかのパッケージによって Debian 上で提供されています。

表12.15 Yacc 互換の LALR パーサー生成ソフトのリスト

パッケージ ポプコン サイズ 説明
bison V:2, I:14 1518 GNU LALR パーサー生成ソフト
byacc V:0.06, I:0.8 143 Berkeley LALR パーサー生成ソフト
btyacc V:0.00, I:0.04 248 byacc に基づいたバックトラッキング機能付きパーサー生成ソフト

bison(1) の入門書は "info bison" の中にあります。

あなた自身の "main()" と "yyerror()" を供給する必要があります。"main()" は、しばしば Flex によって提供される "yylex()" を呼び出す "yyparse()" を呼び出します。

%%

%%

12.7. Autoconf

Autoconf は自動的にソフトウエアーのソースコードパッケージを GNU のビルドシステムを使って種々様々な Unix 的システムに適応させるためのシェルスクリプトを作成するツールです。

autoconf(1) は"configure" という設定プログラムを作成します。"configure" は"Makefile.in" を雛形として使って自動的にカスタム化した "Makefile" を作成します。

12.7.1. プログラムをコンパイルとインストール

[警告] 警告

システムファイルをあなたがコンパイルしたプログラムでインストールする時に上書きしてはいけません。

Debian は"/usr/local/" とか "/opt" 中のファイルに触れません。プログラムをソースからコンパイルする場合、Debian とかち合わないようにそれを "/usr/local/" の中にインストールします。

$ cd src
$ ./configure --prefix=/usr/local
$ make
$ make install # this puts the files in the system

12.7.2. プログラムのアンインストール

オリジナルのソースを保有し、それが autoconf(1)/automake(1) と使用しあなたがそれをどう設定したかを覚えているなら、次のように実行してソフトウエアーをアンイストールします。

$ ./configure ''all-of-the-options-you-gave-it''
# make uninstall

この代わりに、"/usr/local/" の下にだけインストールプロセスがファイルを置いたことが絶対に確実でそこに重要なものが無いなら、次のようにしてその内容を消すことが出来ます。

# find /usr/local -type f -print0 | xargs -0 rm -f

どこにファイルがインストールされるか良く分からない場合には、checkinstall パッケージにある checkinstall(8) を使いアンインストールする場合クリーンなパスとなるようにすることを考えましょう。これは "-D" オプションを使うと Debian パッケージを作成できます。

12.8. 究極の短い Perl スクリプト

どんな AWK スクリプトでも a2p(1) を使えば自動的に Perl に書き換えられますが、1行 AWK スクリプトから1行 Perl スクリプトへの変換は手動変換するのが最良です。

次の AWK スクリプト断片を考えます。

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 ゴルフが面白いです。

12.9. ウェッブ

基本的な対話式動的ウェッブページは次のようにして作られます。

  • 質問 (クエリー) はブラウザーのユーザーに HTML フォームを使って提示されます。

  • フォームのエントリーを埋めたりクリックすることによって次の符号化されたパラメーター付きの 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"".

  • ウェッブサーバー上の CGI プログラム ("program.*" のいずれでも) が環境変数 "$QUERY_STRING" とともに起動されます。

  • CGI プログラムの STDOUT (標準出力) がウエブブラウザーに送られ対話式の動的なウェッブページとして表示されます。

セキュリティー上、CGI パラメーターを解釈する手作りの急ごしらえのプログラムは作らない方が賢明です。Perl や Python にはこのための確立したモジュールが存在します。PHP はこの様な機能とともに提供されます。クライアントでのデーターのストレージの必要がある場合、HTTP クッキーが使われます。クライアントサイドのデーター処理が必要な場合、Javascript が良く使われます。

詳しくは、Common Gateway InterfaceThe Apache Software FoundationJavaScript を参照下さい。

http://www.google.com/search?hl=en&ie=UTF-8&q=CGI+tutorial を URL として直接ブラウザーのアドレスに入れ Google で"CGI tutorial" を検索するとグーグルサーバー上の CGI スクリプトが動いているのを観察する良い例です。

12.10. ソースコード変換

ソースコード変換するプログラムがあります。

表12.16 ソースコード変換ツールのリスト

パッケージ ポプコン サイズ キーワード 説明
perl V:79, I:99 17076 AWK→PERL AWK から PERL へのソースコード変換シフト: a2p(1)
f2c V:0.09, I:1.0 448 FORTRAN→C FORTRAN 77 から C/C++ へのソースコード変換ソフト: f2c(1)
protoize V:0.00, I:0.06 125 ANSI C C コードから ANSI プロトタイプを生成/削除
intel2gas V:0.01, I:0.06 72 intel→gas NASM (Intel フォーマット) から GNU Assembler (GAS) への変換ソフト

12.11. Debian パッケージ作成

Debian パッケージを作りたい場合には、次を読みましょう。

dh-makedh-make-perl 等のようなパッケージングを補助するパッケージもあります。