Shellとシステム(1)

このエントリーをはてなブックマークに追加

Shellとシステム(1)

ここではShellとシステムの情報のやり取りといった単純ですが、実際のプログラム作成時には大事な部分を解説していきます。
まず最初はmain関数の戻り値からです。この値は通常気にせずに"return 0"としている場合がほとんどです。
また、C99では"void main"の形も仕様として認められ(もちろん処理系依存ですが...)、mainの戻り値はさらに意味をなさないと思われがちです。
この意味を理解できればShelを触るときにも役に立つでしょう。

動作環境

最初にShellが関わりますので実行環境を明示しておきます。
ShellはLinuxで標準で使用されているBashを想定しています。
CshやZshなどは対象外とします。Zshは基本Bashの機能も実装しているかとは思いますが、ここはデファクトスタンダードに合わせましょう。


main関数のretrunの扱い

正常終了/異常終了の見分け方

そもそもmain関数でretrunした値にどのような意味があるかはあまり説明されない場合が多く結構謎な部分です。
まずはこのコマンドの実行結果を見てください。

$ ls hoge
hoge
$ echo $?
0
$ LANG=c ls a
ls: cannot access a: No such file or directory
$ echo $?
2

最初の'ls'はファイルが存在しているので、ファイル名を表示して終了しています。
次の'ls'はファイルが存在しないのでエラーメッセージを表示して終了しています。
これら2つの実行ですが、最初は正常終了ですが、2つ目は異常終了です。
この終了状態が正常か異常かを見分ける方法として、"$?"という特殊変数があります。
これが'0'であれば直前に終了したプログラムは正常終了しています。逆に0以外の値であれば異常終了です。


'$?'はshellプログラミングでよく使われるテクニックです。
例えばこのように、特定のプログラムの実行が異常終了した場合だけエラーメッセージを表示するという具合です。

./hoge
if [  $? != 0 ]; then
    #エラー時の対応処理
    echo 'Error:./hoge is faild!'
fi


'$?'は誰が設定している?

'$?'はShellがプログラム終了時に自動で設定する値ですが、そもそもShellはどのようにこの値を取得しているのでしょうか?
実はこれこそが、main関数の戻り値そのものなのです。
実際に試してみましょう。

int main(void)
{
    return 0;
}


$ gcc -o test_return test_return.c
$ ./test_return
$ echo $?
0

0が表示されました。

"return 1"にすると...
int main(void)
{
    return 1;
}


$ gcc -o test_return test_return.c
$ ./test_return
$ echo $?
1

1が表示されました。
答えが分かれば実に簡単です。"ls"はファイル/ディレクトリが無い場合は"return 2"をしていたという事になります。
当然ながら、エラーの場合どういった値を返すかはプログラム次第ですので、"$?"の数字の意味にはルールはありません。

さらに実験を続けます。
int main(void)
{
    return 256;
}


$ gcc -o test_return test_return.c
$ ./test_return
$ echo $?
0

これは驚きです。main関数の戻り値がそのまま"$?"になるはずですが、"256"を返すと"$?"は"0"になりました。
これは実はmain関数の戻り値は"int"型ですが、実際には"unsigned char"型にキャストされているのです。つまり下位2バイトが"$?として使われます。
ですので、"return 512"でも同じ結果になります。


void main

さて、おまけとして処理系依存となりますがgccで"void main"を実行するとどのようになるか実験しましょう。
先に注意事項を書いておきましょう。当然ながらここで行うことは、実装依存です。どのような結果になっても保証は一切ありません。
C99で仕様としては認められましたが、本来のC言語からすると邪道そのものです。本来の動作と食い違いが発生しても仕方がないのです。

void main(void)
{
    return;
}

さて、コンパイルして実行してみましょう。
$ gcc -o void_return void_return.c
$ ./void_return
$ echo $?
1

どうやらgccではエラーと判断されるようですね。
もともと"void main"の形式は組み込み等のmain関数の戻り値が必要のない環境では昔から使われてきました。
もちろん規格から外れているという事を知りつつもです。C99はそういった環境があることを認めて、規格から外れていないという保証を与えたという事です。
Linux等の通常の環境下ではmain関数の戻り値は当然必須ですので、これはGNUの規格と実装の間での精一杯の譲渡なのかもしれません。
ちなみに、exit(3)を使用すれば"void main"でも0を返すことが可能になります。奥が深いですね。


まとめ

いかがでしたでしょうか。mainの戻り値といった普段意識しない非常に単純に見える部分ですが奥が深いことが分かります。
これらの知識を前提として、"/etc/init.d/"以下にあるOS起動時のスクリプトも作成されており非常に重要な部分です。
是非、mainの戻り値の使い分けをしてユーザフレンドリーなプログラムを作成してください。