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/stdin
heredocを通過することはどういう意味ですか?- なぜ他のユーティリティは以前heredocが好き
ruby
かawk
必要なのですか?/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:内部的にパイプを使用するbash
lseek()
関連回答。
両方のansible-playbookコマンドの場合、コマンドがどのように実装されるかによって異なります(したがって、ソースコードを読まないと実際にはわかりません)。一部のコマンドは、ファイルが提供されサポートされていないことを確認しますstdin
。awk
および - などの他のコマンドは、コマンドラインからファイルをruby
予想または指定するように設計されています。stdin
しかし、試すことができるのは、Linuxを使用している場合はそれを実行してstrace ansible-playbook ...<other args>
何を開こうとしているのか、どのシステムコールが発生するのかなどを確認することです。たとえば、tailコマンドを使用すると、実際にファイルでstrace -e open tail /dev/stdin <<< "Jello World"
開こうとしますが、そうでないことがわかります。/dev/stdin
trace -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
ファイルが開きますが、file
2番目の場合(ここでも同様です)では、ファイル名は引数として提供されないため、cat
標準cat
入力ストリームのみが読み取られます(そしてシェルファイルを開くか、ユーティリティの標準入力にこの文書を入力してください。ユーティリティは、提供されたデータがファイル、パイプ、励起文書、または他のデータソースからのものであるかどうかを知る必要はありません。
シェルがこれを実装する方法はここではあまり重要ではありませんが、FIFOを使用するか、実際に一時ファイルを使用することです。