シェルが$(の部分出力を処理するのはなぜですか?

シェルが$(の部分出力を処理するのはなぜですか?

読んでこの行を見ました。IFSに関するブログそれは:

for i in $(<test.txt)

そして、$(<test.txt)ファイルの内容をSTDOUTに印刷することを考えてみましょう。私は間違っているかもしれませんが、好奇心が強いので、シェルでこれを試してみました。したがって、array任意のデータで名前が付けられた任意のファイルが選択され、

まず、1つを実行すると、cat array次のような結果が出ました。

amit@C0deDaedalus:~/test$ 
amit@C0deDaedalus:~/test$ cat array
1)      Ottawa  Canada          345644
2)      Kabul   Afghanistan     667345
3)      Paris   France          214423
4)      Moscow  Russia          128793
5)      Delhi   India           142894

それから$(<array)私には次のものが与えられます。

amit@C0deDaedalus:~/test$ $(<array)
1)      Ottawa  Ca: command not found

入力リダイレクトに使用されることだけがわかりますが、<シェルがここでこれをコマンドとして解釈するものが何であるかを正確には知りません。

シェルでこの奇妙な出力の背後にある概念を説明できる人はいますか?

直す:-

実行すると、set -x次のようになります。

amit@C0deDaedalus:~/test$ $(<array)
+ '1)' Ottawa Canada 345644 '2)' Kabul Afghanistan 667345 '3)' Paris France 214423 '4)' Moscow Russia 128793 '5)' Delhi India 142894
+ '[' -x /usr/lib/command-not-found ']'
+ /usr/lib/command-not-found -- '1)'
1): command not found
+ return 127
amit@C0deDaedalus:~/test$ 

答え1

この$(command)構文はサブシェル環境で実行され、command独自にandに置き換えられますcommandBashのマニュアルによると$(< file)単により速いようです$(cat file)(しかし、POSIX機能ではありません)。

したがって、を実行すると、$(<array)Bashはその置換を実行し、最初のフィールドをコマンド名として使用し、残りのフィールドをコマンドの引数として使用します。

$ $(<array)
1): command not found

コマンド/機能がないため、1)エラーメッセージが表示されます。

ただし、特定のシナリオではIFS変数を変更したため、他のエラーメッセージが表示されることがあります。

$ IFS=n; $(<array)
1)      Ottawa  Ca: command not found

編集1

私の考えでは、あなたのIFSコードが修正されたので、シェルが代わり1) Ottawa Caに実行しようとすることです1)。結局、あなたはIFS関連記事を読んでいます。IFS奇妙な値が出ても驚かないでしょう。

このIFS変数はいわゆる噴射またはフィールド分割。これは、デフォルトでシェルが拡張コンテキスト(または他のコマンドなど)でデータを解析する方法を定義しますread

Bashのマニュアルでこのトピックについて説明します。:

3.5.7 噴射

シェルは、単語分割のために二重引用符内に表示されない引数拡張、コマンド置換、および算術拡張の結果をスキャンします。

シェルは各文字を$IFS区切り文字として扱い、これらの文字をフィールド終端として使用して、他の拡張結果を単語に分割します。設定されていIFSない場合、またはその値がデフォルト値の場合<space><tab><newline>、前の拡張結果の先頭と末尾にある、およびのシーケンスは無視され、<space>開始<tab>または終了以外の文字シーケンスは単語を区切るために使用されます。デフォルト以外の値を使用すると、空白文字が(空白)値内にある限り、単語の先頭と末尾の空白文字、、、およびシーケンスは無視されます。空白以外の文字に隣接する空白文字は別のフィールドです。空白文字シーケンスも区切り文字と見なされます。値が空の場合、単語分割は発生しません。<newline>IFSIFSspacetabnewlineIFSIFSIFSIFSIFSIFSIFS

明示的な空の引数(""または'')は保持され、空の文字列としてコマンドに渡されます。値のないパラメータ拡張により、暗黙的に引用されていないNULLパラメータは削除されます。値のない引数を二重引用符で拡張すると、空の引数が生成されます。これは保持され、空の文字列としてコマンドに渡されます。 NULL以外の拡張単語の一部として引用されたNULL引数が発生すると、NULL引数は削除されます。つまり、単語の分割と空のパラメータの削除後にその単語になります-d''-d

拡張が発生しない場合、分割は行われません。

IFS以下は、コマンドの代替使用のいくつかの例です。

例1:

$ IFS=$' \t\n'; var='hello     world'; printf '[%s]\n' ${var}
[hello]
[world]

$ IFS=$' \t\n'; var='hello     world'; printf '[%s]\n' "${var}"
[hello     world]

どちらの場合も、IFSis <space><tab><newline>(デフォルト)、varis、hello worldおよびステートメントがありますprintf。ただし、最初のケースでは単語の区切りが行われますが、2番目のケースではそうではありません(二重引用符がその動作を抑制するため)。非参照拡張ではトークン化が発生します。

例2:

$ IFS='x'; var='fooxbar'; printf '[%s]\n' ${var}
[foo]
[bar]

$ IFS='2'; (exit 123); printf '[%s]\n' ${?}
[1]
[3]

空白文字もなく、空白文字も含まれていないため、${var}この${?}場合、単語分割が問題にならないと考えることもできます。ただし、これは乱用される可能性があるため、そうではありませんIFSIFSほぼすべての価値を保存し、簡単に乱用することができます。

例3:

$ $(echo uname)
Linux

$ $(xxd -p -r <<< 64617465202d75)
Sat Apr 28 12:46:49 UTC 2018

$ var='echo foo; echo bar'; eval "$(echo "${var}")"
foo
bar

これはトークン化とは関係ありませんが、コードを挿入するためにいくつかの汚いトリックを使用する方法に注意してください。

関連質問:

関連情報