実行中にユーザーを切り替えるスクリプトを作成し、標準のファイルリダイレクトを使用して実行しました。したがって、user-switch.sh
は...
#!/bin/bash
whoami
Sudo su -l root
whoami
bash
で実行すると、期待した動作が得られます
$ bash < user-switch.sh
vagrant
root
ただし、sh
を使用してスクリプトを実行すると、異なる出力が得られます
$ sh < user-switch.sh
vagrant
vagrant
なぜbash < user-switch.sh
とは異なる出力を与えるsh < user-switch.sh
?
Sudo
なしの同様のスクリプトですが、結果は同様です。
_$ cat script.sh
#!/bin/bash
sed -e 's/^/--/'
whoami
$ bash < script.sh
--whoami
$ dash < script.sh
itvirta
_
bash
を使用すると、スクリプトの残りの部分はsed
への入力として送信されます。dash
を使用すると、シェルはスクリプトを解釈します。
これらに対してstrace
を実行すると、dash
はスクリプトのブロックを読み取り(ここでは8 kB、スクリプト全体を保持するのに十分な量を超えます)、次にsed
を生成します。
_read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 8192) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...
_
これは、ファイルハンドルがファイルの最後にあり、sed
が入力を認識しないことを意味します。 dash
内でバッファリングされている残りの部分。 (スクリプトが8 kBのブロックサイズよりも長い場合、残りの部分はsed
によって読み取られます。)
一方、bashは最後のコマンドの最後までシークします。
_read(0, "#!/bin/bash\nsed -e 's/^/--/'\nwho"..., 36) = 36
stat("/bin/sed", {st_mode=S_IFREG|0755, st_size=73416, ...}) = 0
...
lseek(0, -7, SEEK_CUR) = 29
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|...
_
次のように、入力がパイプからのものである場合:
_$ cat script.sh | bash
_
パイプとソケットがシークできないため、巻き戻しを実行できません。この場合、Bashは、一度に1文字ずつ入力を読み取るようにフォールバックして、過剰読み取りを回避します。 ( fd_to_buffered_stream()
in _input.c
_ )各バイトに対して完全なシステムコールを行うことは、原則としてあまり効果的ではありません。実際には、読み取りが大きなオーバーヘッドになるとは思いません。シェルが行うほとんどのことは、まったく新しいプロセスの生成を伴うという事実に。
同様の状況は次のとおりです。
_echo -e 'foo\nbar\ndoo' | bash -c 'read a; head -1'
_
サブシェルは、read
が最初の改行のみを読み取るようにして、head
が次の行を参照できるようにする必要があります。 (これはdash
でも機能します。)
言い換えると、Bashは、スクリプト自体とそれから実行されるコマンドの同じソースの読み取りをサポートするために、追加の長さを使用します。 dash
はしません。 Debianにパッケージ化されているzsh
と_ksh93
_は、Bashでこれに対応しています。
シェルは標準入力からスクリプトを読み取っています。スクリプト内で、標準入力も読み取りたいコマンドを実行します。どの入力がどこに行くのですか? 確実に伝えることはできません。
シェルが機能する方法は、ソースコードのチャンクを読み取って解析し、完全なコマンドを見つけた場合はコマンドを実行して、残りのチャンクと残りのファイルを処理することです。チャンクに完全なコマンドが含まれていない場合(末尾に終了文字がある場合-すべてのシェルが行の最後まで読み取ったと思います)、シェルは別のチャンクを読み取り、以下同様に続きます。
スクリプト内のコマンドが、シェルがスクリプトを読み取っているのと同じファイル記述子から読み取ろうとすると、コマンドは、最後に読み取ったチャンクの後にあるものをすべて検索します。この場所は予測できません。シェルが選択したチャンクサイズによって異なり、シェルとそのバージョンだけでなく、マシン構成、使用可能なメモリなどによっても異なります。
Bashは、コマンドを実行する前に、スクリプト内のコマンドのソースコードの最後を探します。これは、他のシェルがそれを行わないだけでなく、シェルが通常のファイルから読み取っている場合にのみ機能するため、信頼できるものではありません。シェルがパイプから読み取っている場合(例:ssh remote-Host.example.com <local-script-file.sh
)、読み取られたデータは読み取られ、読み取られないようにすることはできません。
スクリプト内のコマンドに入力を渡したい場合は、明示的に行う必要があります。通常は、 ヒアドキュメント を使用します。 (ヒアドキュメントは通常、複数行の入力に最も便利ですが、どの方法でもかまいません。)スクリプトが通常のファイルからシェルへの入力として渡される場合にのみ、作成したコードはいくつかのシェルでのみ機能します。 2番目のwhoami
がSudo …
への入力として渡されると予想した場合は、ほとんどの場合、スクリプトがシェルの標準入力に渡されないことに注意してください。
#!/bin/bash
whoami
Sudo su -l root <<'EOF'
whoami
EOF
この10年間は、Sudo -i root
を使用できることに注意してください。 Sudo su
の実行は過去からのハックです。