不要な猫を気にする必要がありますか?


50

多くのコマンドラインユーティリティは、パイプまたはファイル名引数から入力を取得できます。長いシェルスクリプトのcat場合、特に最初のコマンドに複数行の引数が必要な場合は、チェーンを開始すると読みやすくなります。

比較する

sed s/bla/blaha/ data \
| grep blah \
| grep -n babla

そして

cat data \
| sed s/bla/blaha/ \
| grep blah \
| grep -n babla

後者の方法は効率が悪いですか?その場合、スクリプトが実行されるかどうか、たとえば1秒間に1回実行されるかどうかを考慮するのに十分な違いはありますか?読みやすさの違いはそれほど大きくありません。


30
私は実際に猫のプロセスを開始しない人々が私のシステムよりも、このサイトで役に立たない猫の使用状況に関するお互いを攻撃見て道より多くの時間を費やす
マイケルMrozek

4
@マイケル:100%賛成。ちなみに、私のコンピュータがインスタンス化を無駄にするよりも、一度古いusenet賞にリンクするのに時間がかかりましたcat。しかし、私はここに大きな問題があると思い、コードの可読性が多いですパフォーマンスが優先。ときに速く、実際に書き込むことができますきれいに、なぜか?問題を指摘すると、cat通常、ユーザーはパイプラインとプロセス全般をよりよく理解できるようになります。努力する価値があるので、次回はわかりやすいコードを記述します。
カレブ

3
実際、最初のフォームが気に入らない別の理由があります。パイプラインの先頭に別のコマンドを追加する場合は、引数も移動する必要があるため、編集が面倒です。(もちろん、これは使用する必要があるという意味ではありませんcat。関数とリダイレクトの使用に関するカレブのポイントも同様に解決します。)
Cascabel


1
仕事で夕方です、私の仕事は仕事を拒否しています。stackoverflowを開くと、「不要な猫を気にする必要がありますか?」というタイトルの質問が見つかりました。そして、いくつかのホームレスの動物やプログラマ、それらを供給するかどうかについて熟考参照してください...
ボリスBurkov

回答:


46

「決定的な」答えは、もちろん無用なcat賞の使用によってもたらされます。

catの目的は、ファイルを連結(または「連結」)することです。ファイルが1つだけの場合、何も連結しないと時間の無駄であり、プロセスにコストがかかります。

コードの読み取りが異なるようにcatをインスタンス化すると、必要のないプロセスと入力/出力ストリームのセットが1つだけ増えます。通常、スクリプトの実際のホールドアップは、非効率的なループと実際の処理になります。ほとんどの最新のシステムでは、1つ余分にcatパフォーマンスを犠牲にすることはありませんが、ほとんどの場合、コードを記述する別の方法があります。

ご指摘のとおり、ほとんどのプログラムは入力ファイルの引数を受け入れることができます。ただし、<STDINストリームが予想される場所であればどこでも使用できるシェルビルトインが常に存在し、既に実行されているシェルプロセスで作業を行うことで1つのプロセスを節約できます。

書いた場所で創造性を発揮することさえできます。通常、次のような出力リダイレクトまたはパイプを指定する前に、コマンドの最後に配置されます。

sed s/blah/blaha/ < data | pipe

しかし、そのようにする必要はありません。それも最初に来ることができます。たとえば、サンプルコードは次のように記述できます。

< data \
    sed s/bla/blaha/ |
    grep blah |
    grep -n babla

スクリプトの読みやすさが懸念事項であり、コードが乱雑であるために行を追加するとわかりやすくなる場合は、コードcatをクリーンアップする他の方法があります。スクリプトを後で簡単に理解できるようにするために私がよく使用するのは、パイプを論理セットに分割して関数に保存することです。スクリプトコードは非常に自然になり、ピップラインのどの部分でもデバッグが容易になります。

function fix_blahs () {
    sed s/bla/blaha/ |
    grep blah |
    grep -n babla
}

fix_blahs < data

その後、続行できfix_blahs < data | fix_frogs | reorder | format_for_sqlます。そのように読み取れるパイプラインは本当に簡単に追跡でき、個々のコンポーネントはそれぞれの機能で簡単にデバッグできます。


26
私は<fileそれが命令の前に来ることを知りませんでした。これで私の問題はすべて解決しました!

3
@Tim:BashとZshはどちらもサポートしていますが、見苦しいと思いますが。コードがきれいで保守可能であることを心配しているときは、通常、関数を使用してクリーンアップします。私の最後の編集を参照してください。
カレブ

8
@Tim <fileは、コマンドラインのどこにでも配置できます:<file grep needleまたはgrep <file needleまたはgrep needle <file。例外は、ループやグループ化などの複雑なコマンドです。リダイレクトは、閉じるdone/ }/ )/ etcの後に来る必要があります。@Calebこれは、すべてのBourne / POSIXシェルに適用されます。そして、私はそれがいことに同意しません。
ジル 'SO-悪である停止

9
@Gillesは、bashであなたを置き換えることができる$(cat /some/file)$(< /some/file)同じことを行いますが、プロセスを生成回避します、。
cjm

3
$(< /some/file)移植性が限られていることを確認するだけです。bashで動作しますが、たとえばBusyBoxの灰やFreeBSD shでは動作しません。おそらく、最後の3つのシェルはすべて親類であるため、ダッシュでも機能しません。
-dubiousjim

22

以下に、いくつかの欠点の概要を示します。

cat $file | cmd

以上

< $file cmd
  • 最初に、注意:$file上記の周りに(意図的に議論の目的で)二重引用符がありません。の場合cat、それは常にを除いて問題ですzsh。リダイレクトの場合、それはbashorの問題でksh88あり、他の一部のシェルでは(スクリプトではなく)インタラクティブな場合のみです。
  • 最もよく挙げられる欠点は、余分なプロセスが発生することです。if cmdが組み込みの場合、などの一部のシェルでは2プロセスでさえあることに注意してくださいbash
  • cat組み込みのシェルを除いて、パフォーマンスの面ではまだ実行されています(もちろん、追加のコマンドがロードされ、初期化されています(およびリンクされているライブラリも同様))。
  • それでも、性能面では、大きなファイルのために、その手段は、システムが交互にスケジュールする必要がありますcatし、cmdプロセスと常にいっぱいとパイプのバッファを空にします。場合でもcmdない1GB大規模なread()時にシステムコールを、コントロールが間を行き来する必要がありますcatし、cmdパイプは一度に多くのデータの数キロバイト以上保持することはできませんので。
  • 一部cmdのs(などwc -c)はcat | cmd、stdinが単なるパイプであるため、stdinが処理できない通常のファイルである場合、いくつかの最適化を実行できます。catパイプを使用するとseek()、ファイル内で使用できないことも意味します。tacまたはtailなどのコマンドの場合、cat入力全体をメモリに保存する必要があるため、パフォーマンスに大きな違いが生じます。
  • cat $file、とさえその多くの正しいバージョンは、cat -- "$file"いくつかの特定のファイルのような名前のために正常に動作しません-(あるいは--help始まるか何か-忘れている場合--)を。の使用を主張する場合、信頼性のcatためにcat < "$file" | cmd代わりに使用する必要があります。
  • $file読み取り用に開くことができない場合(アクセスが拒否され、存在しません...)、< "$file" cmd一貫したエラーメッセージを(シェルによって)報告し、実行しませんcmdcat $file | cmd実行は継続しますcmdが、stdinは空のファイルのように見えます。また< file cmd > file2、のようなものでfile2は、開くfileことができない場合は上書きされません。

2
パフォーマンスについて:このテストでは、ストリームでほとんど処理を行っていない限り、違いは1 pct程度であることが示されていますoletange.blogspot.dk/2013/10/useless-use-of-cat.html
Ole

2
@OleTange。別のテストを次に示しますtruncate -s10G a; time wc -c < a; time cat a | wc -c; time cat a | cat | wc -c。構想に入る多くのパラメーターがあります。パフォーマンスのペナルティは、0〜100%の範囲で指定できます。いずれにせよ、ペナルティはマイナスになるとは思いません。
ステファンシャゼラス16

2
wc -cショートカットがあるため、非常にユニークなケースです。代わりに行う場合wc -w、これはgrep私の例と同等です(つまり、処理が非常に少ない-「<」違い生む可能性がある状況です)。
オレ丹下

@OleTange(wc -wLinux 4.9 amd64のCロケールの1GBのスパースファイルでも)では、マルチコアシステムではcatアプローチが23%時間がかかり、1つのコアにバインドする場合は5%時間がかかります。複数のコアがデータにアクセスすることで発生する余分なオーバーヘッドを示します。パイプのサイズを変更し、異なるデータを使用し、実際のI / Oにsplice()を使用するcat実装を使用すると、さまざまな結果が得られる可能性があります。いずれにしてもcat助けにはなりません。
ステファンシャゼラス

1
1GBのファイルを使用しwc -wている私にとっては、単純な単純なgrepの場合、約2%から15%の差です。そして、奇妙なことに、NFSファイル共有上にある場合、catgist.github.com/rdp/7162414833becbee5919cda855f1cb86)からパイプされた場合、実際に読むのに20%速くなります...
rogerdpack

16

<fileパイプラインの終わりに置くことcat fileは、最初に持つことよりも読みにくくなります。自然英語は左から右に読みます。

<fileパイプラインの開始点を置くことは、猫よりも読みにくくなります。単語は、シンボル、特に間違った方向を指しているように見えるシンボルよりも読みやすいです。

を使用catすると、command | command | command形式が保持されます。


私は同意します。<一度使用するとコードが読みにくくなります。マルチパイプラインの構文の一貫性を破壊するからです。
-A.ダニシェウスキー

あなたがにエイリアスを作成することにより、読みやすさを解決することができ@Jim <:このようにalias load='<'して、例えばを使用load file | sed ...。エイリアスは、実行後にスクリプトで使用できますshopt -s expand_aliases
-niieani

1
はい、エイリアスについて知っています。ただし、このエイリアスはシンボルを単語に置き換えますが、読者が個人のエイリアス設定を知っている必要があるため、あまり移植性がありません。
ジム

8

ここでの他の答えが直接対処していないように見えることの1つは、catこのように使用しても「作業を行わない無関係な猫プロセスが生成される」という意味で「無駄」ではないということです。「不必要な作業のみを行うcatプロセスが生成される」という意味では役に立ちません。

これら2つの場合:

sed 's/foo/bar/' somefile
<somefile sed 's/foo/bar/'

シェルはsedプロセスを開始し、somefileまたはstdinから(それぞれ)読み取り、処理を行います-改行に達するまで読み取り、その行の最初の「foo」(存在する場合)を「bar」に置き換えてから印刷しますstdoutとループへのその行。

の場合:

cat somefile | sed 's/foo/bar/'

シェルはcatプロセスとsedプロセスを生成し、catのstdoutをsedのstdinにワイヤリングします。catプロセスは、ファイルから数キロバイトまたはメガバイトのチャンクを読み取り、それをその標準出力に書き込みます。そこで、sed sommandは上記の2番目の例のようにそこから取得します。sedがそのチャンクを処理している間、catは別のチャンクを読み取り、次の作業のためにsedの標準出力に書き込みます。

言い換えれば、catコマンドを追加することで必要となる余分な作業は、余分なcatプロセスを生成するだけでなく、ファイルのバイトを1回ではなく2回読み書きする余分な作業でもあります。現在、実質的に言えば、現代のシステムでは、それほど大きな違いはありません。システムが数マイクロ秒の不必要な作業を行う可能性があります。しかし、既にパワー不足のマシンで潜在的にそれを使用している人々に配布することを計画しているスクリプトの場合、多くの反復で数マイクロ秒が加算される可能性があります。


2
追加のを使用するオーバーヘッドのテストについては、oletange.blogspot.dk / 2013/10 / useless-use-of-cat.htmlを参照してくださいcat
オレ丹下

@OleTange:偶然これに出くわして、あなたのブログを訪れました。(1)内容を(ほとんど)英語で見ながら、デンマーク語(「クラシスク」、「フリップカード」、「マガシン」、「モザイク」、「シデビャルケ」、「Øjebliksbillede」)に一連の単語を見る、「Tidsskyder」、「Blog-arkiv」、「Om mig」、「Skrevet」、および「Vis kommentarer」(ただし、「Tweet」、「Like」、およびCookieバナーは英語です)。これについてご存知ですか、それはあなたの管理下にありますか?(2)グリッド線が不完全であるため(2a)テーブルの読み取りに問題があり、(2b)「Diff(pct)」の意味がわかりません。
G-Manは「Reinstate Monica」と言います

blogspot.dkはGoogleによって実行されます。blogspot.comに置き換えてみてください。"差分(PCT)"はMSとなるcatことなく、MSで割ったcatパーセント(例えば264ミリ秒/ 216秒= 1.22 = 122パーセント= 22%遅くなるとcat
オレ丹下
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.