次の2つを使用して同じ結果を得ることができますbash
。
echo 'foo' | cat
そして
cat <<< 'foo'
私の質問は、使用されたリソースの点でこれらの2つの違いは何ですか、どちらが優れていますか?
私の考えは、パイプを使用するときに追加のプロセスとecho
パイプを使用することですが、ここで文字列はファイル記述子のみを使用することですcat
。
答え1
パイプはカーネルファイルシステムで開かれたファイルで、ディスクから通常のファイルにアクセスできません。特定のサイズまで自動的にバッファリングされ、いっぱいになると最終的にブロックされます。ブロックデバイスで生成されたファイルとは異なり、パイプはキャラクタデバイスと非常によく似ているため、通常はサポートされていません。lseek()
そして、それから読み取ったデータは、通常のファイルのように再読み込みできません。
ここにある文字列は、マウントされたファイルシステムで生成された一般的なファイルです。シェルはファイルを作成し、そのディスクリプタを保持しながら、独自のファイルシステムリンクをすぐに削除します。(だから削除)ファイルにバイトを書き込むか、読み込む前に。カーネルは、ファイルのすべての記述子がすべてのプロセスによって解放されるまで、ファイルに必要なスペースを保持します。そのような記述子を読む子供がそうすることができる場合は、以下を使用して元に戻すことができます。lseek()
そしてもう一度読んでください。
どちらの場合も、タグ<<<
とタグは|
ファイル記述子を表しますが、必ずしもファイル自体を表すわけではありません。次のようにすると、何が起こっているのかをよりよく理解できます。
readlink /dev/fd/1 | cat
...または...
ls -l <<<'' /dev/fd/*
2つのファイル間の最も重要な違いは、here-string / docがほぼワンタイムイベントであることです。つまり、シェルは読み取り記述子を提供する前にサブファイルにすべてのデータを書き込みます。一方、シェルは適切な記述子でパイプを開き、パイプ記述子を管理するためにサブプロセスをフォークすることによって作成/読み取りされます。同時に両端から。
しかし、これらの違いは一般的に言えば本当。私が知る限り(実際にはそれほど遠くない)これは、1つの例外を除いて、ここでドキュメントリダイレクトの文字列の省略形を処理する<<<
ほとんどすべてのシェルに当てはまります。<<
yash
。ただしyash
、、、、およびその他のバリエーションは、ここで説明されているようにパイプの使用をサポートする傾向がbusybox
あるため、これらのシェルでは実際に2つの間に違いはありません。dash
ash
いいですね。 2つの例外があります。今考えてみると、実際にはパイプ作業をまったく実行せず、ソケットを使用してビジネス全体を処理します。たとえksh93
他の人と同じように一時|
ファイルを削除しますが、<<<*
さらに重要なことは、パイプラインのさまざまな部分を1つに統合することです。サブシェル環境これはPOSIXの曲線表現です。少なくともサブシェルのように動作します。だからフォークも作らないでください。
実はベンチマークです @PSkocik(これはとても便利です)ここでの結果は大きく異なる場合がありますたくさん理由はほとんどの実装に関連しています。ここで、ドキュメント設定の最大の要素は、ターゲット${TMPDIR}
ファイルシステムの種類と現在のキャッシュ構成/可用性、および書き込むデータの量です。パイプの場合、必要なフォークに対してコピーが作成されるため、シェルプロセス自体のサイズになります。それだけbash
です。悪いパイプライン設定中($(
コマンド)
置換を含む)- 規模が大きいため非常に遅いですが、使用してもksh93
ほとんど違いはありません。
以下は、パイプがパイプ用のサブシェルを分割する方法を示す別の小さなシェルの断片です。
pipe_who(){ echo "$$"; sh -c 'echo "$PPID"'; }
pipe_who
pipe_who | { pipe_who | cat /dev/fd/3 -; } 3<&0
32059 #bash's pid
32059 #sh's ppid
32059 #1st subshell's $$
32111 #1st subshell sh's ppid
32059 #2cd subshell's $$
32114 #2cd subshell sh's ppid
pipe_who()
パイプライン呼び出しで報告された内容と現在のシェルで実行されているレポートの違いは、(
拡張時に)
親シェルのpidを宣言するサブシェルの指定された動作によるものです。サブシェルは確かに別々のプロセスですが、$$
特別なシェルパラメータはこの情報の信頼できるソースではありません。それにもかかわらず、サブシェルのサブシェルは自分の 。bash
$$
sh
$PPID
答え2
ベンチマークに代わるものはありません。
pskocik@ProBook:~
$ time (for((i=0;i<1000;i++)); do cat<<< foo >/dev/null; done )
real 0m2.080s
user 0m0.738s
sys 0m1.439s
pskocik@ProBook:~
$ time (for((i=0;i<1000;i++)); do echo foo |cat >/dev/null; done )
real 0m4.432s
user 0m2.095s
sys 0m3.927s
$ time (for((i=0;i<1000;i++)); do cat <(echo foo) >/dev/null; done )
real 0m3.380s
user 0m1.121s
sys 0m3.423s
大容量データの場合:
TENMEG=$(ruby -e 'puts "A"*(10*1024*1024)')
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do echo "$TENMEG" |cat >/dev/null; done )
real 0m42.327s
user 0m38.591s
sys 0m4.226s
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do cat<<< "$TENMEG" >/dev/null; done )
real 1m26.946s
user 1m23.116s
sys 0m3.681s
pskocik@ProBook:~
$ time (for((i=0;i<100;i++)); do cat <(echo "$TENMEG") >/dev/null; done )
real 0m43.910s
user 0m40.178s
sys 0m4.119s
ダクトバージョンは、インストールコストが高くなるように見えますが、最終的にはより効率的です。
答え3
あなたが気づいたように、管路子プロセスを作成し、ここにある文字列確かに。これは場合によっては重要です。
以下を比較してみてください。
しかし:
grep $USER /etc/passwd | IFS=: read user x1 uid gid x2 home shell
echo $user $uid $gid
何も表示しません。
IFS=: read user x1 uid gid x2 home shell <<<$(grep $USER /etc/passwd)
echo $user $uid $gid
示す:
myuser 1000 1000