web-dev-qa-db-ja.com

zsh:コマンド置換はその親からstdinを継承しません

次のコマンドについて考えてみます。

_seq 5 | grep $(tail -n1) <(seq 9)
_

zshで実行する場合1

_tail: error reading 'standard input': Input/output error
_

bashで同じように実行すると、次のように出力されます。

_5
_

OK。コメントで説明されているように、コマンド置換$(tail -n1)はその親からstdinを継承します。しかし、なぜそれがzshで起こらないのですか?
これはzshのみのものですか、それとも他のシェルも行うことですか?どこに文書化されていますか?


ここで、_zsh -c_を介して同じコマンドを実行すると、次のようになります。

_zsh -c 'seq 5 | grep $(tail -n1) <(seq 9)'
_

同じエラーメッセージを出力する代わりに、_tail -n1_の後に停止し、ユーザー入力を待つので、入力すると

_19
2
4
_

次にヒット Ctrl+D、印刷します

_4
_

何が起きてる ?


1:これは、重要な場合、zsharchlinux _5.3.1_を使用します。

4
don_crissti

bash/kshにあることに気付くでしょう

_echo foo | echo "$(cat)"
_

fooを出力しますが、

_<<< foo echo "$(cat)"
_

そうではありません。

最初のケースでは、$(cat)は子プロセスで展開され、そのstdinがパイプからリダイレクトされた後、最終的にechoを実行します。

2番目のケースでは、リダイレクトの前に$(cat)が展開されます。

パイプとリダイレクトは別のものです。パイプには、いくつかのリダイレクトが含まれますが、コマンドを並行して開始することも含まれます。これは、各パイプコンポーネント内のリダイレクトの前の早い段階で発生します。

zsh

_$ sleep 1 | ps -jfH $(ps -fH >&2)
UID        PID  PPID  C STIME TTY          TIME CMD
chazelas  2495  2494  0 20:59 pts/1    00:00:00 /bin/zsh
chazelas 31201  2495  0 21:20 pts/1    00:00:00   sleep 1
chazelas 31202  2495  0 21:20 pts/1    00:00:00   ps -fH
UID        PID  PPID  PGID   SID  C STIME TTY          TIME CMD
chazelas  2495  2494  2495  2495  0 20:59 pts/1    00:00:00 /bin/zsh
chazelas 31201  2495 31201  2495  0 21:20 pts/1    00:00:00   sleep 1
chazelas 31203  2495 31201  2495  0 21:20 pts/1    00:00:00   ps -jfH
_

今回は、コマンド置換が親シェルによって拡張されていることに気付くでしょう。

覚えておくべきことの1つは、zshでは、特に_mult_ios_オプション(デフォルトで有効)に関しては、パイプがリダイレクトのように扱われることです。

あなたがするとき:

_echo foo > file | tr o e
_

foofiletrの両方に移動します。

に:

_uname | cat < /etc/issue
_

catには、unameの出力と_/etc/issue_のコンテンツの両方が供給されます。したがって、zshでは、_<_/_>_とパイプからのリダイレクトは同じ段階で発生する必要があります。できれば拡張後。

いずれにせよ、あなたはいつでもすることができます:

_echo foo | { echo "$(cat)"; }
_

zshbash/kshの両方で、いつでもできるように:

_{ echo "$(cat)"; } <<< foo
_

bashzshの両方で。


の原因について:

_tail: error reading 'standard input': Input/output error
_

エラー。対話型シェルでは、コマンド置換は親で行われるため、ターミナルのフォアグラウンドプロセスグループでは行われません。

tailは、親シェルのプロセスグループで実行されます。そのシェルがセッションリーダーである場合、それは 孤立したプロセスグループ になるため、tailがttyデバイスから読み取ろうとすると、EIOで失敗します。

zshがセッションリーダーではなかった場合。たとえば、別のシェルからzshを開始した場合、プロセスグループはSIGTTINを受け取ります。メインのシェルプロセスはそれを無視しますが、tailは最終的に中断されます。

6