親シェルhere-documentはdashのサブコマンドとは機能しませんが、bashが機能するのはなぜですか?

親シェルhere-documentはdashのサブコマンドとは機能しませんが、bashが機能するのはなぜですか?

dash私はdebianstretchで実行しており、次のコマンド(および)はすべてbashbashに入力されます。決してユーザーとして実行されない
ようですwhoamiテスト以下のコードのようにダッシュで表示されます。

$ sudo dash << 'end'
> su test
> whoami
> end
root
$ sudo bash << 'end'
> su test
> whoami
> end
test

答え1

次の例を考えてみましょう。

$ cat f
grep pos /proc/self/fdinfo/0
IFS= read -r var
echo A
echo B
printf '%s\n' "var=$var"
$ bash < f
pos:    29
B
var=echo A
$ dash < f
pos:    85
A
B
var=

ご覧のとおり、コマンドを実行すると、stdinの場所はファイルgrepの終わりで、dashコマンドの後に続く改行文字の直後にあります。grepbash

コマンドecho Aはによって実行されますが、dashその場合bashは入力として与えられますread

dashコマンドを実行する前に、入力全体(実際にはテキストブロック)を一度に1行ずつ読みます。bash

これを行うには、bash改行文字を通過しないように一度に1バイトずつ読み込む必要がありますが、入力が通常のファイルである場合(上記のファイルの場合と同じでドキュメントにも適用されfます)、ここでbashはパイプのdash使用中に一時ファイルにします。実装します。 Linuxでわかるbashように、チャンクで読んで行末に逆追跡して最適化します。strace

$ strace -e読み取り、lseek bash < f
[...]
lseek(0, 0, SEEK_CUR) = 0
読み取り (0, "grep pos /proc/self/fdinfo/0\nIFS"...,85) = 85
lseek(0, -56, SEEK_CUR) = 29
ポジション:29
[...]

$strace -e 読み込み、lseek ダッシュ < f
読み取り (0, "grep pos /proc/self/fdinfo/0\nIFS"...,8192) = 85
ポジション:85
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=12422, si_uid=1000, si_status=0, si_utime=0, si_stime=0} ---
読み取り(0,"",1) = 0
[...]

stdinがターミナルデバイスの場合、それぞれはread()ターミナルから送信された行を返すため、通常とbash同様の動作を見ることができますdash

あなたの場合は、次のようにすることができます。

sudo dash << 'end-of-script'
su test <<"end"
whoami
end
end-of-script

またはより良い:

sudo sh -c '
  su test -c whoami
'

または、より良い方法は次のとおりです。

sudo -u test whoami

答え2

BashとDashはstdinの入力を異なる方法で扱います。コマンドを実行するとき(suここでは)、Bashはコマンドを実行させた後のstdinのタイムセクションパイプで1つの単語を読み取るか、逆に見て読み取る位置を慎重に保ちます。入力がファイルから来ていることを確認してください。ダッシュは気にせず、ブロック全体を読みます。

これは、標準入力とその中の読み取り位置がシェルとサブプロセスとの間で共有されるため、重要です。

したがって、Bashを使用するときを読み、su test\nwhoami\n最初の改行の後に戻って始めます。suこれがwhoami\n入力に表示されます。

Dashの場合は、読み込みをsu test\nwhoami\n開始しsu、入力が表示されずに終了し、Dashが開始されますwhoami


ここでも同じことがわかります。 Dashを使用するとdateコマンドを実行し、空のread入力を取得しますが、Bashを使用すると行はreadですx

$ cat test.sh
read x
date
echo "variable x = '$x'"
$ cat test.sh | bash
variable x = 'date'
$ cat test.sh | dash
Sat Aug 24 12:25:23 EEST 2019
variable x = ''

説明するとPOSIXの説明shBashの慎重な行動が必要であることは正しいです。一方、ダッシュの余裕のあるアプローチはこれには適していません。

STDIN
[...]シェルが標準入力を使用し、標準入力も使用するコマンドを呼び出す場合、シェルはコマンドが実行を開始したときに標準入力ファイルポインタが読み取っているコマンドの直後を指すことを確認する必要があります。呼び出されたコマンドで読み取る文字がシェルで消費されるように事前に読み取ってはいけません。

それほど価値があるほど、BusyboxのシェルはDashと同様に機能し、私がテストした他のすべてのシェルはBashの機能を果たします。

関連情報