bash スクリプトの罠

こんにちは、masm11 です。お久しぶりです。 今回は bash スクリプトの罠を一つご紹介したいと思います。

症状

以下のような bash スクリプトを作成します。

#!/bin/bash

sleep 5

5秒間 sleep して終了するだけのプログラムです。

実行すると、

$ ./test.sh
$ 

5秒後にプロンプトに戻ります。

次は、端末をもう一枚開き、test.sh を実行中に、以下のように test.sh を書き換えてみます。

$ echo echo kita >> test.sh
$ 

するとどうでしょう。

$ ./test.sh
kita
$ 

後から追加したコードも実行されてしまっています。

解明

いったい何が起きているのでしょうか?

こういう時にはシステムコールをトレースすると便利です。

以下のように実行します。

$ strace -f -o log ./test.sh
kita
$ 

こうすると、どんなシステムコールが実行され、どんな返り値だったかが、log ファイルに 出力されます。

この log ファイルから重要そうな箇所だけを抜き出してみました。

24863 openat(AT_FDCWD, "./test.sh", O_RDONLY) = 3
24863 read(3, "#!/bin/bash\n\nsleep 5\n", 80) = 21
24863 lseek(3, 0, SEEK_SET)             = 0
24863 dup2(3, 255)                      = 255
24863 fstat(255, {st_mode=S_IFREG|0755, st_size=21, ...}) = 0
24863 lseek(255, 0, SEEK_CUR)           = 0
24863 read(255, "#!/bin/bash\n\nsleep 5\n", 21) = 21
24863 wait4(-1,  <unfinished ...>
24863 <... wait4 resumed>[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 24864
24863 read(255, "echo kita\n", 21)      = 10
24863 read(255, "", 21)                 = 0

最初の 24863 はプロセスIDで、その右に実行したシステムコールの内容が出力されています。

wait4 と書いてあるところが、sleep コマンドの終了を待っているところです。その後に、

24863 read(255, "echo kita\n", 21)      = 10

と続いています。なんと、ここでスクリプトの続きを読んでいました。

まとめ

bash スクリプトは実行中にもスクリプトを read していることがわかりました。 実行中に書き換えると、場合によってはとんでもない目に遭うことが想像できます。

bash スクリプトは実行中に書き換えないようにしましょう!