Bashのファイル読み取りコマンドの置き換えについて

Bashのファイル読み取りコマンドの置き換えについて

Bashが次の行を正確に処理する方法を理解しようとしています。

$(< "$FILE")

Bashのマニュアルページによると、これは次のようになります。

$(cat "$FILE")

2行目から推論の流れに従うことができます。 Bashは入力コマンドの置換時に変数拡張を実行して$FILE値をに渡し、catはOrderの内容を出力します。$FILEcat$FILE

ただし、上記の最初の行の場合は、次のように解釈します。 Bashはで変数置換を実行し、Bashは標準入力から読み取るために$FILE開きます。$FILE何らかの方法で標準入力を標準出力にコピーします。、コマンド置換が完了し、Bashは生成された標準出力を実行しようとします。

$FILEコンテンツがstdinからstdoutにどのように移動するかを説明できますか?

ベストアンサー1

$(<file)(ksh93でも、`<file`およびで使用されます)は、およびからコピーされた${<file;}Kornシェルの特殊演算子です。コマンドの置換と非常に似ているようですが、そうではありません。zshbash

POSIXシェルでは、単純なコマンドは次のとおりです。

< file var1=value1 > file2 cmd 2> file3 args 3> file4

すべての部分はオプションであり、リダイレクトのみ、コマンドのみ、割り当てのみ、または組み合わせることができます。

リダイレクトがありますが、コマンドがなければリダイレクトは行われますが(したがってaが> file開かれ、切り捨てられますfile)、何も起こりません。だから

< file

読み取り用に開かれますfileが、コマンドがないため、何も起こりません。その後、file終了しました。$(< file)簡単なものならコマンドの置き換えすると、空の状態に展開されます。

内部にPOSIX仕様、inにリダイレクトのみが含まれている$(script)場合script指定されていない結果が発生しました。これは Korn シェルの特別な動作を可能にするためのものです。

ksh(ここでテスト済みksh93u+)でスクリプトに1つしか含まれていない場合簡単なコマンド(コメントは前後に許可されますが)リダイレクト(コマンドなし、割り当てなし)のみが含まれ、最初のリダイレクトがstdin(fd 0)の場合(<<<または<<<)リダイレクトのみを入力すると、次のようになります。

  • $(< file)
  • $(0< file)
  • $(<&3)$(0>&3)実際にこれは実際には同じ演算子なので)
  • $(< file > foo 2> $(whatever))

しかし:

  • $(> foo < file)
  • ...でもない$(0<> file)
  • ...でもない$(< file; sleep 1)
  • ...でもない$(< file; < file2)

それから

  • 最初のリダイレクトを除くすべてのリダイレクトは無視されます(解析され削除されます)。
  • file/heredoc/herestring(または同様のものを使用する場合は、ファイル記述子から読み取ることができるすべての項目)の内容から<&3末尾の改行文字を引いたものに展開されます。

$(cat < file)それを除いては使用感に似ています。

  • 読み取りはシェルではなく内部で行われます。cat
  • 配管や追加のプロセスは必要ありません。
  • 上記と同じ理由で、内部コードはサブシェルで実行されないため、すべての変更(たとえば、$(<${file=foo.txt})または$(<file$((++n))))はその後も保持されます。
  • 読み取りエラー(ファイルを開いたりファイルディスクリプタをコピーしたときに発生するエラーではありません)は自動的に無視されます。

では、ファイル入力リダイレクト(または、no、...)zshが1つしかない場合にのみ特殊動作がトリガされることを除いて同じです。<file0< file<&3<<<here< a < b

しかし、他のシェルをシミュレートすることに加えて、つまり、< file コマンドなしで入力リダイレクトが1つしかない場合は、コマンド置換の外部でzsh実行$READNULLCMD(デフォルトではポケットベル)で、リダイレクトが多いか< file<&3、、、、.. )(デフォルト)であるため、対応<<<textする特殊演算子として認識されなくても、呼び出して実行したように動作します。<a <b>file<a >b$NULLCMDcat$(<&3)kshcat

ただし、の内容kshは次に展開されますが、$(< a < b)inの内容は次の内容に展開されます。azsha そしてb(またはそのオプションが無効になっている間b)コピーされ、空の状態に展開されます。multios$(< a > b)ab

bash 同様の演算子がありますが、いくつかの違いがあります。

  • コメントは以前は許可されていますが、その後は許可されません。

    echo "$(
       # getting the content of file
       < file)"
    

    動作しますが:

    echo "$(< file
       # getting the content of file
    )"
    

    何も膨らませなかった。

  • のように、zshstdinのファイルリダイレクトは1つだけですが、aへのフォールバックはないため、リダイレクトは行われますが、$READNULLCMD拡張子は空です。$(<&3)$(< a < b)

  • 何らかの理由でbash呼び出していない場合でも、catプロセスを分岐してファイルの内容をパイプするため、他のシェルよりも最適化がはるかに少なくなります。実際に組み込まれたのと同じです$(cat < file)catcat

  • 上記の理由により、変更は失われます($(<${file=foo.txt})たとえば、上記の場合は割り当てが$file失われます)。

((適用))は、コンテンツを読むためのより効率的な方法ですbashIFS= read -rd '' var < filezshテキスト変数にファイルを置きます。また、末尾の改行文字を保存できるという利点もあります。また、($mapfile[file]モジュールでは、通常のファイルのみ)バイナリファイルでも機能します。zshzsh/mapfile

pdksh ベースのバリアントにはkshksh93 に比べていくつかの変更があります。興味深いことにmksh(pdksh派生シェルの1つ)から

var=$(<<'EOF'
That's multi-line
test with *all* sorts of "special"
characters
EOF
)

ここにある文書のように、一時ファイルやパイプを使わずに、ここの文書の内容(末尾改行なし)が拡張され、有効な複数行参照構文になるため最適化されました。

kshzshおよびのすべてのバージョンへの移植性のために、コメントを避け、変数の変更が維持されても維持されない可能性があることを覚えておくのがbash最善です。$(<file)

おすすめ記事