/dev/stdinとHeredocを使用してコマンドラインからファイルを渡す

/dev/stdinとHeredocを使用してコマンドラインからファイルを渡す

heredocsをコマンドラインユーティリティにファイルに渡す方法についての理論が疑問に思います。

最近、私はファイルをheredocに渡すことができることを発見しました。

たとえば、

awk '{ split($0, arr, " "); print arr[2] }' <<EOF
foo bar baz
EOF
bar

これは次の理由で私にとって有利です。

  • Heredocsは、複数行入力の読みやすさを向上させます。
  • コマンドラインからファイルの内容を渡すためにすべてのユーティリティフラグを覚えておく必要はありません。
  • 特定のファイルで一重引用符と二重引用符を使用できます。
  • シェルの拡張を制御できます。

たとえば、

ruby <<EOF
puts "'hello $HOME'"
EOF
'hello /Users/mbigras'

ruby <<'EOF'
puts "'hello $HOME'"
EOF
'hello $HOME'

何が起こったのかわかりません。シェルはheredocをheredocの値と内容が同じファイルと見なしているようです。私はこのスキルをcatと一緒に使ってみましたが、それでも何が起こっているのかわかりません。

cat <<EOL
hello world
EOL
hello world

私はcat印刷ファイルの内容を知っているので、おそらくこのheredocは一種の一時ファイルです。

「コマンドラインプログラムに区切り文字を渡す」とき、正確に何が起こっているのか混乱します。

以下は使用例です。可能なスクリプト。 PlayBookをheredocとしてユーティリティに渡しましたが、次の方法で失敗しましたecho $?

ansible-playbook -i localhost, -c local <<EOF &>/dev/null
---
- hosts: all
  gather_facts: false
  tasks:
    - name: Print something
      debug:
        msg: hello world
EOF
echo $?
5

しかし、同じheredocをユーティリティに渡しましたが、/dev/stdin成功する前に

ansible-playbook -i localhost, -c local /dev/stdin <<EOF &>/dev/null
---
- hosts: all
  gather_facts: false
  tasks:
    - name: Print something
      debug:
        msg: hello world
EOF
echo $?
0
  • 「区切り文字をファイルに渡す」と正確に何が起こりますか?
  • 最初のバージョンはansible-playbook失敗しましたが、2番目のバージョンが成功するのはなぜですか?
  • /dev/stdinheredocを通過することはどういう意味ですか?
  • なぜ他のユーティリティは以前heredocが好きrubyawk必要なのですか?/dev/stdin

答え1

「区切られた文書をファイルに渡す」と正確に何が起こりますか?

あなたはそうではありません。ここでは標準入力を提供します。パイプのように。あなたの模範

awk '{ ... }' <<EOF
foo bar baz
EOF

まったく同じ

echo foo bar baz | awk '{ ... }'

awk、、、catおよびruby標準入力から読み取ったもの(コマンドラインに読み取ることができるファイル名が指定されていない場合)。これは実装の選択です。

anisble-playbookを含む最初のバージョンは失敗しますが、2番目のバージョンが成功するのはなぜですか?

ansible-playbookデフォルトでは標準入力からは読み込まれませんが、ファイルパスが必要です。デザインの選択です。

/dev/stdin/dev/fd/0現在のプロセスについて話す方法であるシンボリックリンクである可能性が高いです。ファイル記述子#0(標準入力)。これはカーネル(またはシステムライブラリ)によって公開されます。このansible-playbookコマンドは/dev/stdin通常のファイルシステムファイルのように開き、最終的に独自の標準入力を読み込みます。それ以外の場合は無視されます。

おそらくFD 1と2への/dev/stdoutリンクがあります/dev/stderr。これは、出力をどこに入れるかを知りたい場合にも使用できます。

区切り文字の前に/ dev / stdinを渡すのはなぜですか?

これはコマンドのパラメータですansible-playbook

Rubyやawkなどの他のユーティリティでheredocの前に/ dev / stdinを要求しないのはなぜですか?

デフォルトではパイプラインで使用するように設計されているため、標準入力をデザイン選択として読み込みます。同じ理由で標準出力に書き込みます。

答え2

Here-docsで何が起こるのかは、シェルがここでどのように実装するかによって異なります例:(dash. -doc:内部的にパイプを使用するbashlseek()関連回答

両方のansible-playbookコマンドの場合、コマンドがどのように実装されるかによって異なります(したがって、ソースコードを読まないと実際にはわかりません)。一部のコマンドは、ファイルが提供されサポートされていないことを確認しますstdinawkおよび - などの他のコマンドは、コマンドラインからファイルをruby予想または指定するように設計されています。stdin

しかし、試すことができるのは、Linuxを使用している場合はそれを実行してstrace ansible-playbook ...<other args>何を開こうとしているのか、どのシステムコールが発生するのかなどを確認することです。たとえば、tailコマンドを使用すると、実際にファイルでstrace -e open tail /dev/stdin <<< "Jello World"開こうとしますが、そうでないことがわかります。/dev/stdintrace -e open tail

答え3

here-documentは、のようにコマンドの標準入力にリダイレクトされます。これは、リダイレクトファイルの内容を<使用できる任意の場所でhere-documentの内容をリダイレクトできることを意味します。<POSIX規格ここにあるドキュメントとここにリストされているその他のリダイレクト演算子

Ansible の例ではansible-playbookファイル名が必要なため、デフォルトでは標準入力ストリームから読み込まれません。ファイル名に指定してから、この文書を標準入力として提供することで、ユーティリティ/dev/stdinでこの制限を回避できます。 「file /dev/stdin」は常に現在のプロセスの標準入力データストリームを含む。

ruby他のawk多くのユーティリティは標準入力から読み取られます。〜しない限りファイル名はコマンドラインにあります。

だからあなたは技術的に「シェルがheredocをheredoc値と同じ内容を持つファイルと考えているようです」と言うと、これは間違っています。ファイル名があり、検索可能であるという点では、ファイルのようには機能せず、標準入力のデータストリームとして機能します。少なくともユーティリティの観点からは。

違いは次のとおりです。

cat file

そして

cat <file

最初の場合はcatファイルが開きますが、file2番目の場合(ここでも同様です)では、ファイル名は引数として提供されないため、cat標準cat入力ストリームのみが読み取られます(そしてシェルファイルを開くか、ユーティリティの標準入力にこの文書を入力してください。ユーティリティは、提供されたデータがファイル、パイプ、励起文書、または他のデータソースからのものであるかどうかを知る必要はありません。

シェルがこれを実装する方法はここではあまり重要ではありませんが、FIFOを使用するか、実際に一時ファイルを使用することです。

関連情報