bash 3のパイプと文字列の違い

bash 3のパイプと文字列の違い

私が実行した場合:

IFS=':' cat <<< $(echo $PATH)

その後、「:」は単語分割に使用され、出力には「:」の代わりにスペースが含まれます。しかし、以下を実行すると:

echo $PATH | IFS=':' cat

そうではありません。

パイプの私の理解は、fd 1 ofをfd 0 ofにa | b接続することです。そしてherestringsの私の理解は、彼らが文字列を.fd 0に直接書き込むということです。どちらの場合も、同じ内容をcatのfd 0に書き込む必要があるようです。aba <<< stringa

これは、2つのコマンド間に違いがあってはならないことを意味しますが、違いを示すため、私が誤解している部分が明らかにあるはずです。

diff <(echo $PATH | IFS=':' cat) <(IFS=':' cat <<< $(echo $PATH))

私は何もしようとせず、それを終了するためにカットして貼り付けることができるテキストを要求するのではありません。私は私の理解と矛盾する状況を説明し、私の誤解の説明を求めました。

これら2つのコマンドが異なる出力を生成する理由を誰かが説明できますか?


ちょうどbash 4で試してみましたが、違いはありませんでした。それで、「Bashのバージョンは何ですか?」と尋ねました...それで、質問をbash 3に制限するように編集しました。私はbash 4について尋ねることではありません。

繰り返しますが、質問は「私は何も考えずに切り取り、貼り付けることができるものを誰かが私に与えることができますか」ではありません。問題は「これら2つの違いは何ですか?」です。

答え1

一部のソフトウェアの以前のバージョンで奇妙な動作が発生した場合は、修正されたバグ/バグ機能が原因である可能性があります。通常、最新のソフトウェアバージョンを使用することをお勧めします。

IFS新しい価値

IFS=':' cat <<< $(echo $PATH)

のように単語分割動作に影響を与えてはいけませんIFS=':' echo $PATH。または、同じ新しい値をで使用しないでくださいvarvar=xyz cat <<< "$var"var=xyz echo "$var"

しかし、これはBash 4.0より前のバージョンのhere-stringで発生し、私がテストした他のシェルでは発生しません。他の拡張機能の仕組みと、私が試した他のシェルと一致しないという事実は、これらの動作がないという事実がバグであることを示唆しています。また、Bash 4.1に変更されたので、Bash管理者もこれを行う必要があるとは思わないようです。

CHANGESファイルには、bash-4.1-alphahere-stringsではなくhere-docsのみを明示的に言及するための変更が含まれていますが、そうでなければ関連性があるように聞こえます。

jj. Fixed a bug that caused variable expansion in here documents to look in
    any temporary environment.

バグのある動作は、文字列付きコマンドとパイプ付きコマンドの違いを説明します。


多少関連する質問で、ここで文字列が最初にトークン化されるという事実もバグです。通常のcmd <<< $var割り当てでは複数の文字列を割り当てることができないように、here-stringを介して複数の異なる文字列を渡す方法がないためfoo=$var

z.  Bash no longer splits the expansion of here-strings, as the documentation
    has always said.

とにかく、これら2つのコマンドは次のようになります。

echo $foo | somecmd 
somecmd <<< $(echo $foo)

2つのバグを修正しても全く同じではありません。コマンド置換は末尾の改行をすべて削除し、ここで文字列に正確に1を追加しますが、パイプは渡されたデータを変更しません。

基本的なメカニズムも非常に異なります。では、cmd2 <<< $(cmd1)シェルがcmd1開始する前に出力全体を読み取る必要がありますが、両方の出力が並列に実行され、シェルがデータcmd2に触れるcmd1 | cmd2必要はありません。

また、いつものように、変更されていない変数値をどこでも使用するには、拡張を二重引用符で囲む必要があります。

答え2

効果的ではありませんIFS=':' catIFS環境変数としてインポートしていますcat。割り当てはcatプロセスにのみ表示されます。cat何もしないでくださいIFS

最初の例のこのバージョンは分割動作を再現します。

(IFS=':'; cat <<< $(echo $PATH))

IFS範囲が割り当てられるように、サブシェルで実行するためにその周りに括弧を入れました。IFSコマンド置換の結果$(echo ...)を拡張すると、値が有効なため機能します。

この行動は*確かに文書と一致しません。 Bash のマニュアルページでは、<<<word単語の区切りは行われていないことが示されています。しかし、それがまさに分割の目的ですIFS。それは単語の分割です。

実際に起こったことはこれを$PATHコマンドに置き換えるのは、以下に基づいて単語分割をecho実行することです。IFS

cat <<< $(echo $PATH)コマンドを実行する前にまず拡張してください$PATH。これがIFSうまくいくところです。これechoにより、空間的に経路が分割される。出力全体がパラメータwordになり、分割されなく<<< wordなりました。

各実験の動作を考えてみましょう。

# <<< prevents word splitting

$ (IFS=':'; abc='a:b:c'; cat <<<$abc )  # <<< prevents word splitting
a:b:c

# quotes prevent word splitting

$ (IFS=':'; abc='a:b:c'; cat <<<$(echo "$abc"))
a:b:c

# lack of quotes allows word splitting (same as $PATH example)

$ (IFS=':'; abc='a:b:c'; cat <<<$(echo $abc))
a b c

# Proof that a:b:c is already split coming from echo

$ (IFS=':'; abc='a:b:c'; cat <<<$(echo $abc | tr ' ' -))
a-b-c

関連情報