終了ステータスとは?
スクリプトが終了したときに、スクリプトを実行したツールに返される値。
// 終了ステータス "0"
int main(){
return (0);
}
// 終了ステータス "1"
int main(){
return (1);
}
基本的な終了ステータスの種類は以下の通りです。[1]
終了ステータス | 意味 |
---|---|
0 | 正常終了 |
1 | 一般的なエラー |
2 | シェルビルトインの誤用 |
126 | 呼び出されたコマンドが実行できない |
127 | コマンドが見つからない "command not found" |
128 | 無効な終了引数 |
128+n | シグナル終了ステータス |
130 | Control-Cでスクリプトを終了 |
255 | 終了ステータスが範囲外 (範囲は0 ~ 255) |
終了ステータスの確認方法
shellの特殊変数 $? に終了ステータスが入っています。
echoコマンドで確認することができます。
echo $?
先ほどのコードをコンパイルして終了ステータスを確認してみます。
bash-3.2$ cat test.c
int main(){
return (1);
}
bash-3.2$ gcc test.c
bash-3.2$ ./a.out
bash-3.2$ echo $?
1
しっかりと1が終了ステータスに格納されているのがわかります。
パイプがあるときの終了ステータス
パイプがある際に終了ステータスは
コマンドの実行速度などには関係なく、
パイプを繋いだ最後のコマンド(一番右のコマンド)の終了ステータスになります。
以下の場合は、右のコマンドの終了ステータスが表示されるので1が返ってきます。
bash-3.2$ exit 0 | exit 1
bash-3.2$ echo $?
1
シグナルでコマンドが終了してもこの原則は変わりません。
# Control-Cでスクリプトを終了 -> 終了ステータス130
bash-3.2$ cat
^C
bash-3.2$ echo $?
130
# echo abc が正常終了しているから0が返ってきます。
bash-3.2$ cat | echo abc
abc
^C
bash-3.2$ echo $?
0
また、多段パイプでも同じことが言えます。
bash-3.2$ exit 0 | exit 21 | exit 42
bash-3.2$ echo $?
42
実装
以下では、C言語で多段パイプを大枠を実装しています。
使用関数は、以下のものを使用することにより実現できます。
pipe, fork, dup, dup2, wait, open, close
概要程度になりますが、以下のように実装することができます。
以下の実装では、forkした際に得られるプロセスIDをpid[index]に格納し、
最後にwhile文を通ったそのコマンドこそがlast_pidになるはずです。
そして、そのlast_pidをwaitすることにより、exit_statusを得られます。
#include <libc.h>
int main(void)
{
int cmd_num = 2; // number of command
int index = 0;
int exit_number = 0;
int pid[cmd_num];
int last_pid;
while (index < cmd_num)
{
pipe(pid);
pid[index] = fork();
last_pid = pid[index];
if (pid[index] == 0) // child proccess
{
// 子プロセス用のシグナルにする (signal, sigaction)
// connect pipe (dup2, close)
// connect redirect (dup2)
// do command (execve)
// put the exit_number
exit(exit_number);
}
index++;
}
index = 0;
int exit_status;
int status;
while (index < cmd_num) // wait in parent proccess
{
if (wait(&status) == last_pid)
exit_status = WEXITSTATUS(status);
index++;
}
return (exit_status);
}
追伸
私がbashの簡易実装を行なったレポジトリです。(たぶん似たコードがあるはずですw)↓
よければスターをつけていただければ幸いです。。。