LoginSignup
0
0

More than 1 year has passed since last update.

Bash スクリプトが逐次読込されている件の詳細

Last updated at Posted at 2021-12-30

bash スクリプトの実行中上書き動作について
このパーサは fgets(2) で読み込まれつつ実行される為、一括でファイルが読み込まれている訳ではない。

普通の fgets の実装ならまとまった単位でバッファに read してそのバッファから 1 行ごとに返るだろうので、ファイルサイズが大きくなければ一括で読まれることになるのでは・・?

と思っていたところ下記を見まして、

実際に strace してみるとそのような挙動になっていたので、興味本位で Bash のソースを探って見ました。

Bash のソースは bash-5.1.8.tar.gz です。

スクリプトの読み込み

スクリプトの読み込みは input.cwith_input_from_buffered_stream で、init_yy_io に設定している buffered_getchar で行われています。最終的にこれは lib/sh/zread.czread によって read(2) が呼ばれます。

while ((r = read (fd, buf, len)) < 0 && errno == EINTR)
{
    // ...snip...
}

len の最初のサイズは input.cfd_to_buffered_stream で次のように決められています。

if (fstat (fd, &sb) < 0)
{
    // ...snip...
}
size = (fd_is_seekable (fd)) ? min (sb.st_size, MAX_INPUT_BUFFER_SIZE) : 1;
if (size == 0)
  size = 1;

ファイルがシーク可能ならファイルサイズと MAX_INPUT_BUFFER_SIZE=8172 の小さい方、シーク不可なら 1 です。

シーク不可となるのは例えばプロセス置換のパイプです。bash <(cat a.sh) などとすると 1 バイトずつ read されます。これは strace ですぐ確認できます。

strace -e trace=read bash <(cat a.sh)

一方、普通のファイルならシーク可能なので、ファイルサイズが 8172 以下なら一括でまとめて read されます。

だとすれば実行中のスクリプトを上書きしても(スクリプトが 8172 バイト以下なら)問題なさそうですけど・・?

外部コマンド実行時のシーク

Bash が外部コマンドを実行するため(execute_cmd.cexecute_command)に子プロセスを作成するとき(jobs.cmake_child)、それに先立ってスクリプトのオフセットを実行しようとしているコマンドの直後の位置に lseek(2) で戻しています。

これは input.csync_buffered_stream で行われています。

chars_left = bp->b_used - bp->b_inputp;
if (chars_left)
  lseek (bp->b_fd, -chars_left, SEEK_CUR);
bp->b_used = bp->b_inputp = 0;

例えば次のようなスクリプトの場合、

/bin/echo 1
/bin/echo 2
/bin/echo 3

最初にファイル全体が read されますが、その後 /bin/echo 1 の直後まで lseek で戻り、次は改めてその位置から read されます。

この動作は外部コマンドを呼ぶときだけで、次のようにビルドインの echo だとこの lseek は発生しません。

echo 1
echo 2
echo 3

例えば、次のスクリプトは最初の1行目を実行した時点でスクリプトの内容が空になった後、改めて2行目を読もうとして読めなくて、その結果なにも表示されません。

/bin/echo -n >a.sh
echo ok

ですが次のスクリプトだとシークが発生せず、最初にファイルが一括で読まれた内容で実行されるため ok が表示されます。

echo -n >a.sh
echo ok

さいごに

Bash が何故そんな動作をしているのかは謎。意図的に実行中の自身を上書きできるようにしているのかと思ったけれども、それだとビルドインコマンドが対象外なのが謎ですし、謎。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0