パイプラインを使用しているときに、読み取ったコマンドをサブシェルに配置する必要があるのはなぜですか[重複]

Nov 30 2020

このコマンドdf .は、現在使用しているデバイスを表示できます。例えば、

me@ubuntu1804:~$ df .
Filesystem     1K-blocks    Used Available Use% Mounted on
/dev/sdb1       61664044 8510340  49991644  15% /home

次に、文字列を取得します/dev/sdb1

私はこのように試しましたが、うまくいきませんでした:df . | read a; read a b; echo "$a"、このコマンドは私に空の出力を与えました。しかしdf . | (read a; read a b; echo "$a")、期待どおりに機能します。

私は今ちょっと混乱しています。

それ(read a; read a b; echo "$a")がサブシェルであることは知っていますが、なぜここでサブシェルを作成する必要があるのか​​わかりません。私の理解として、x|yの出力xをの入力にリダイレクトしますy。なぜread a; read a b; echo $a入力を取得できないのにサブシェルは取得できるのですか?

回答

8 muru Dec 01 2020 at 03:10

ここでの主な問題は、コマンドを正しくグループ化することです。サブシェルは二次的な問題です。

x|yの出力xをの入力にリダイレクトしますy

はい、しかし、x | y; zの出力をリダイレクトするつもりはないxの両方にyz

ではdf . | read a; read a b; echo "$a"、パイプラインのみ接続さdf .read a、他のコマンドは、パイプラインに接続されていません。あなたはグループに持っていreadます。■一緒にdf . | { read a; read a b; }またはdf . | (read a; read a b)パイプラインは、それらの両方に接続されるため。

ただし、サブシェルの問題が発生します。パイプライン内のコマンドはサブシェルで実行されるため、変数を設定しても親シェルには影響しません。したがって、echoコマンドはreadsと同じサブシェルにある必要があります。だから:df . | { read a; read a b; echo "$a"; }

パイプラインのコマンドはとにかくサブシェルで実行されるため、ここで使用する( ... ){ ...; }、特に違いはありません。

3 glennjackman Dec 01 2020 at 03:10

代替は、使用することですプロセスsubstitionを:

{ read header; read filesystem rest; } < <(df .)
echo "$filesystem"

<(...)プロセス置換は(サブシェルで)含まれているスクリプトを実行しますが、それは、ファイル名のように働くので、あなたが最初に必要<ブレーススクリプトに(スクリプトの出力である)の内容をリダイレクトします。グループ化されたコマンドは、現在のシェルで実行されます。

これを読みやすくするのは難しいかもしれませんが、中括弧とプロセス置換に任意の空白を入れることができます。

{
    read header
    read filesystem rest
} < <(
    df .
)
echo "$filesystem"

また、外部ツールを使用してファイルシステムを抽出する方が簡単な場合があります。

filesystem=$( df . | awk 'NR == 2 {print $1}' )
2 StephenHarris Dec 01 2020 at 03:11

あなたの最初のコマンド

df . | read a; read a b; echo "$a"

効果的に次のように解釈されます

( df . | read a ) ; read a b; echo "$a"

したがって、パイプラインはread aコマンドにのみフィードします。

パイプラインから複数の読み取りが必要なため、コマンドをグループ化する必要があります。

今ではサブシェルである必要はありません。それはグループ化である可能性があります。

bash-4.2$ df | { read a ; read a b ; echo $a ; }
devtmpfs

より一般的には、ループが必要になる場合があります

bash-4.2$ df | while read a > do > read a b > echo $a
> done
devtmpfs
tmpfs
/dev/vda3
/dev/vdb

bashサブシェルで実行されているパイプラインの右側に2番目の問題があるため$a $bwhileループの外部で値にアクセスできませんが、それは別の問題です。