読んでいるときにこの行を見ました IFSのブログ つまり:
_for i in $(<test.txt)
_
そして、$(<test.txt)
がファイルの内容をSTDOUTに出力すると考えました。私はこれが間違っているかもしれませんが、好奇心からシェルでやろうとしました。そこで、ランダムなデータを持つarray
という名前のランダムなファイルを取得しました。
最初に私にこれを与えた_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$
_
$(command)
構文はサブシェル環境でcommand
を実行し、それ自体をcommand
の標準出力に置き換えます。そして、 Bash Manualが言うように 、$(< file)
は$(cat file)
と同等の高速です(ただし、POSIX機能ではありません)。
したがって、$(<array)
を実行すると、Bashはその置換を実行し、最初のフィールドをコマンドの名前として使用し、残りのフィールドをコマンドの引数として使用します。
$ $(<array)
1): command not found
1)
コマンド/関数がないので、エラーメッセージが出力されます。
ただし、特定のシナリオでは、おそらくIFS変数を変更したために、別のエラーメッセージが表示されます。
$ IFS=n; $(<array)
1) Ottawa Ca: command not found
私の推測では、IFS
が何らかの形で変更されたため、シェルは1) Ottawa Ca
ではなく1)
を実行しようとしました。結局のところ、あなたはIFS
関連の記事を読んでいました。あなたのIFS
が奇妙な値になってしまったとしても、私は驚かないでしょう。
IFS
変数は、単語分割またはフィールド分割として知られているものを制御します。これは基本的に、拡張コンテキストでシェルによって(またはread
などの他のコマンドによって)データがどのように解析されるかを定義します。
3.5.7単語分割
シェルは、Word分割の二重引用符内で発生しなかったパラメーター展開、コマンド置換、および算術展開の結果をスキャンします。
シェルは
$IFS
の各文字を区切り文字として扱い、他の展開の結果をこれらの文字をフィールドターミネータとして使用して単語に分割します。IFS
が設定されていない場合、またはその値がデフォルトの<space><tab><newline>
である場合、最初と最後の<space>
、<tab>
、および<newline>
のシーケンス前の展開の結果の一部は無視され、先頭または末尾にないIFS
文字のシーケンスは単語を区切るのに役立ちます。IFS
の値がデフォルト以外の場合、空白文字space
、tab
、およびnewline
のシーケンスはの最初と最後で無視されます。空白文字がIFS
(IFS
空白文字)の値である限り、Word。IFS
空白以外のIFS
内の文字は、隣接するIFS
空白文字とともに、フィールドを区切ります。IFS
空白文字のシーケンスも区切り文字として扱われます。IFS
の値がnullの場合、Wordの分割は発生しません。明示的なnull引数(
""
または''
)は保持され、空の文字列としてコマンドに渡されます。値を持たないパラメーターの展開に起因する、引用符で囲まれていない暗黙のnull引数は削除されます。値のないパラメーターが二重引用符で囲まれている場合、null引数が生成され、保持されて空の文字列としてコマンドに渡されます。引用符で囲まれたnull引数が、展開がnull以外のWordの一部として表示される場合、null引数は削除されます。つまり、Wordの分割とnull引数の削除の後、Word-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]
どちらの場合も、IFS
は<space><tab><newline>
(デフォルト値)、var
はhello world
、そしてprintf
ステートメントがあります。ただし、前者の場合は単語分割が実行されますが、後者の場合は実行されないことに注意してください(二重引用符はその動作を禁止するため)。 単語の分割は、引用符で囲まれていない展開で発生します。
例2:
$ IFS='x'; var='fooxbar'; printf '[%s]\n' ${var}
[foo]
[bar]
$ IFS='2'; (exit 123); printf '[%s]\n' ${?}
[1]
[3]
${var}
も${?}
も空白文字を含まないため、このような場合は単語の分割は問題にならないだろうと思われるかもしれません。しかし、IFS
が悪用される可能性があるため、これは正しくありません。 IFS
は事実上すべての値を保持でき、悪用されやすいです。
例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
これはWordの分割とは何の関係もありませんが、いくつかの汚いトリックを使用してコードを挿入する方法に注意してください。
関連する質問: