strace
を使用する場合 実行時にシェル スクリプトがどのように実行されるかを確認できます。
例
このシェル スクリプトがあるとします。
$ cat hello_ul.bash
#!/bin/bash
echo "Hello Unix & Linux!"
strace
を使用して実行する :
$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$
strace.log
の中を見てみましょう ファイルは以下を明らかにします。
...
open("./hello_ul.bash", O_RDONLY) = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR) = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET) = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD) = -1 EBADF (Bad file descriptor)
dup2(3, 255) = 255
close(3)
...
ファイルが読み込まれると、実行されます:
...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20) = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8) = 0
read(255, "", 40) = 0
exit_group(0) = ?
上記では、スクリプト全体が単一のエンティティとして読み込まれ、その後実行されているように見えることがはっきりとわかります。 「現れる」 少なくとも Bash の場合は、ファイルを読み込んで実行します。実行中にスクリプトを編集できると思いますか?
注: しかし、しないでください!実行中のスクリプト ファイルをいじってはいけない理由を理解するために読んでください。
他の通訳者はどうですか?
しかし、あなたの質問は少しずれています。必ずしもファイルのコンテンツをロードするのは Linux ではなく、コンテンツをロードするのはインタープリターです。そのため、ファイルを完全にロードするか、一度にブロックまたは行単位でロードするかは、インタープリターがどのように実装されるかにかかっています。
では、なぜファイルを編集できないのでしょうか?
ただし、はるかに大きなスクリプトを使用すると、上記のテストが少し誤解を招くことに気付くでしょう。実際、ほとんどのインタープリターはファイルをブロック単位でロードします。これは、ファイルのブロックをロードして処理し、別のブロックをロードする多くの Unix ツールではかなり標準的です。 grep
に関して少し前に書いたこの U&L Q&A で、この動作を確認できます。 、タイトル:grep/egrep は毎回どのくらいのテキストを消費しますか?.
例
次のシェル スクリプトを作成するとします。
$ (
echo '#!/bin/bash';
for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done
) > ascript.bash;
$ chmod +x ascript.bash
このファイルの結果:
$ ll ascript.bash
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash
次のタイプのコンテンツが含まれています:
$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"
strace
を使用して上記と同じ手法を使用してこれを実行すると、 :
$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192
ファイルが 8KB 単位で読み込まれていることがわかります。そのため、Bash やその他のシェルは、ファイルをブロックごとに読み込むのではなく、ファイル全体を読み込まない可能性があります。
参考文献
- #!マジック、さまざまな Unix フレーバーでのシバン/ハッシュバン メカニズムの詳細
これは、OS 依存というよりもシェル依存です。
バージョンによっては ksh
スクリプトをオンデマンドで 8k または 64k バイトのブロック単位で読み取ります。
bash
スクリプトを 1 行ずつ読みます。ただし、行の長さが任意である可能性があるため、次の行の先頭から毎回 8176 バイトを読み取って解析します。
これは、単純な構造、つまり一連の単純なコマンド用です。
シェル構造のコマンドが使用されている場合 (受け入れられた回答が考慮されない場合 ) for/do/done
のように ループ、case/esac
スイッチ、ヒア ドキュメント、括弧で囲まれたサブシェル、関数定義など、および上記の任意の組み合わせの場合、シェル インタープリターは構造の最後まで読み取り、構文エラーがないことを最初に確認します。
同じコードが何度も何度も読み取られる可能性があるため、これはいくぶん非効率的ですが、このコンテンツは通常キャッシュされるという事実によって軽減されます。
シェル インタープリタが何であれ、実行中にシェル スクリプトを変更することは非常に賢明ではありません。シェルはスクリプトの任意の部分を自由に再読み取りできるため、同期が外れると予期しない構文エラーが発生する可能性があります。
ksh93 が問題なく読み取ることができる非常に大きなスクリプト構造を保存できない場合、bash がセグメンテーション違反でクラッシュする可能性があることにも注意してください。
これは、スクリプトを実行しているインタープリターがどのように機能するかによって異なります。カーネルが行うことは、実行するファイルが #!
で始まることに注意することだけです 、基本的に残りの行をプログラムとして実行し、実行可能ファイルを引数として渡します。そこにリストされているインタープリターがそのファイルを行ごとに読み取る場合 (対話型シェルが入力内容に対して行うように)、それが得られます (ただし、複数行のループ構造が読み取られ、繰り返しのために保持されます)。インタプリタがファイルをメモリに丸呑みして処理する場合 (おそらく、Perl や Pyton のように中間表現にコンパイルする)、ファイルは実行前に完全に読み込まれます。
その間にファイルを削除すると、インタープリターがファイルを閉じるまでファイルは削除されません (いつものように、最後の参照がディレクトリ エントリまたはファイルを開いたままにしておくプロセスであるかどうかにかかわらず、ファイルは消えます)。