望ましくない猫に興味を持たなければなりませんか?

望ましくない猫に興味を持たなければなりませんか?

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

比較する

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

そして

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

後者のアプローチはそれほど効率的ではありませんか?それでは、スクリプトを実行するかどうか(たとえば、1秒に1回)を気にするほど違いはありますか?読みやすさの違いは大きくありません。

答え1

もちろん、「最終」の答えは次のとおりです。cat賞の無駄な使用

catの目的は、ファイルをリンク(または「接続」)することです。単純なファイルの場合、他のものとリンクするのは時間の無駄であり、プロセスのコストがかかります。

コードを読みやすくするためにcatをインスタンス化すると、プロセスと不要な入力/出力ストリームセットのみが追加されます。多くの場合、スクリプトの実際の障害は、非効率的な屋根ふきと実際の処理です。ほとんどの最新システムでは、追加の方法はパフォーマンスcatに影響を与えませんが、ほとんど常にコードを書く他の方法があります。

すでに知っているように、ほとんどのプログラムは入力ファイルの引数を受け入れることができます。ただし、STDINストリームが必要なときはいつでも、すでに<実行されているシェルプロセスでタスクを実行してプロセスを保存する組み込みシェルを使用することは常に可能です。

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

sed s/blah/blaha/ < data | pipe

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

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

スクリプトの読みやすさを重視し、コードが複雑すぎて行を追加すると、理解しcatやすくなると思われる場合は、コードを整理する別の方法があります。私がよく使う1つの方法は、パイプラインを論理セットに分割して関数に格納することです。これにより、後でスクリプトを理解しやすくなります。これにより、スクリプトコードが非常に自然になり、パイプラインのすべての部分をデバッグするのが簡単になります。

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

fix_blahs < data

その後、続行できますfix_blahs < data | fix_frogs | reorder | format_for_sql。これらのパイプラインは本当に理解しやすく、個々のコンポーネントはその機能で簡単にデバッグできます。

答え2

以下は、いくつかの欠点を要約したものです。

cat $file | cmd

超過

< $file cmd
  • まず、注:上記の二重引用符がありません(意図的にこの議論の目的のために)$file。リダイレクトの場合、catこれは常に問題ですzsh。リダイレクトの場合bashやPOSIXモードを含む他の一部のシェルでは、スクリプトではなく対話型でのみ問題が発生しますksh88bash

  • 最も一般的に言及される欠点は、追加のプロセスが作成されることです。cmd一部のシェルでは、組み込みの場合、2つのプロセスも作成されますbash

  • それでもパフォーマンスの観点からは、cat組み込みシェルに加えて(もちろんロードおよび初期化(およびリンクされているライブラリ)と共に)追加のコマンドが実行されます。

  • パフォーマンスの観点から見ると、これは大容量ファイルの場合、システムがスケジュールとプロセスを交互catcmdし、パイプバッファを継続的に埋め、空にする必要があることを意味します。大規模なシステムコールをcmd実行しても、パイプは一度に数キロバイトを超えるデータを保持できないため、制御を前後に切り替える必要があります。1GBread()catcmd

  • cmd一部のs(例えば)は、標準入力が通常のファイルであるときにいくつかの最適化を実行できますが、標準入力はパイプであるため、wc -cそうすることはできません。パイプcat | cmdの場合、catこれはseek()ファイルに含めることができないことを意味します。tacあるいは、このようなコマンドでは、入力全体をメモリに保存する必要があるため、tailパフォーマンスに大きな違いがあります。cat

  • cat $file、さらに正確なバージョンであっても、一部の特定のファイル名(または次に始まるファイル名を忘れた場合)ではcat -- "$file"正しく機能しません。誰かがそれを使用しようとすると、信頼性を確保するためにおそらくそうする必要があります。---help---catcat < "$file" | cmd

  • 読み取り用に開くことができない場合$file(アクセス拒否、存在しない...)、< "$file" cmd一貫したエラーメッセージが報告され(シェルで)いいえrun cmd、whileはcat $file | cmdまだruncmdですが、標準入力は空のファイルのように見えます。これはまた、このような場合に< file cmd > file2開けないとfile2破壊されないことを意味します。file

    つまり、cmd file > file2出力ファイルが常に(シェルを介して)開く順序ではなく、入力ファイルと出力ファイルが開く順序を選択できます。今後入力ファイル(by cmd)を使用する場合、これはほとんど望ましくありません。

    cmd1 < file | cmd2 > file2ただし、場所cmd1と場所、およびリダイレクトを同時に独立して実行するのには役立たず、開けないときに壊れたり実行されたりするのを防ぐために、または作成する必要がありますcmd2{ cmd1 | cmd2; } < file > file2(cmd1 | cmd2 > file2) < filefile2cmd1cmd2file

答え3

<fileパイプの端に配置するのは、cat file最初に配置するよりも読みにくいです。自然な英語は左から右に読みます。

<fileパイプの先頭を最初に置くのもcatより読みやすくなると言いたいです。単語は、記号、特に間違った方向を指すように見える記号よりも読みやすくなります。

cat保存された書式を使用しますcommand | command | command

答え4

ここで他の答えが直接解決できないことの1つは、「cat不要な作業を実行する猫プロセスを作成する」ため、「機能しない無関係な猫プロセスを生成する」ため、このように使用することは「役に立たない」ことですです。 " "意味、それは役に立たない。

どちらの場合も:

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

シェルは、いくつかのファイルまたは標準入力(それぞれ)から読み取られるsedプロセスを開始し、いくつかの処理を実行します。改行文字に出会うまで読み、行の最初の「foo」を「bar」(存在する場合)に置き換えます。その後、その行を標準出力に印刷して繰り返します。

次の場合:

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

シェルはcatプロセスとsedプロセスを生成し、catの標準出力をsedの標準入力に接続します。 catプロセスはファイルからキロバイトまたはメガバイト単位のチャンクを読み取り、それを標準出力に書き込みます。上記の2番目の例に示すように、sed sommandはそれを取得します。 sedがこのブロックを処理している間、catはsedが続行できるように他のブロックを読み取り、それを標準出力に書き込みます。

つまり、コマンドを追加するために必要な追加のタスクは、追加プロセスを作成する追加のcatタスクだけでなく、catファイルバイトを1回ではなく2回読み書きする追加のタスクです。現実的に言えば、現代のシステムではこれは大きな違いをもたらしません。システムに数マイクロ秒間不要な操作を実行させることもできます。ただし、既にパフォーマンスが不足しているコンピュータでスクリプトを使用している人にスクリプトを配布したい場合は、複数の反復で数マイクロ秒が経過する可能性があります。

関連情報