1. ホーム
  2. shell

[解決済み] 猫の無駄遣い?

2022-07-13 20:39:13

質問

これはおそらく多くのFAQにあるもので、使用する代わりに

cat file | command

(これはcatの無駄な使い方と呼ばれる)正しい方法であるはずです。

command < file

2 番目の、quot;正しい" 方法では、OS は追加のプロセスを生成する必要はありません。

それを知っていながら、私は 2 つの理由で無駄な猫を使い続けました。

  1. より美的に - データが左から右へだけ一様に動くのが好きです。そして、より簡単に cat を他の何かで置き換えるのも簡単です ( gzcat , echo ...)、2つ目のファイルを追加したり、新しいフィルタを挿入する( pv , mbuffer , grep ...).

  2. 私は、場合によっては速くなるかもしれないと感じました。 2つの処理があるため、より速く、1つ目の処理( cat ) が読み込みを行い、2 番目が何かを行うからです。 そして、それらは並列に実行することができるので、より速く実行することができます。

私のロジックは正しいですか(2番目の理由)?

どのように解決するのですか?

私は、今日までこの賞のことを知りませんでした。 UUOC をつけようとした新人がいました。それは cat file.txt | grep foo | cut ... | cut ... . 私は彼に自分の考えを伝え、そうして初めて、彼がくれた賞の起源とその慣習に言及したリンクを訪れました。さらに検索を続けた結果、この質問にたどり着きました。意識して考えてみたものの、残念ながら、どの回答にも私の根拠は含まれていませんでした。

私は、彼に答えるにあたって、身構えるつもりはなかったのです。結局のところ、私の若い時代には、コマンドを次のように書いていたことでしょう。 grep foo file.txt | cut ... | cut ... というのは、頻繁にある単一の grep を繰り返すたびに、ファイル引数の配置を学習し、最初のものがパターンで、後のものがファイル名であることが既知の知識となっているからです。

を使うのは意識的な選択でした。 cat を使うことを意識的に選択したのです。

後者の理由がより重要であるため、私はそれを最初に出します。私がパイプラインをソリューションとして提供するとき、それが再利用可能であることを期待します。パイプラインが他のパイプラインの末尾に追加されたり、他のパイプラインに接続されたりすることはよくあることです。そのような場合、grepにファイルの引数を与えると再利用性が損なわれます。 無言で ファイル引数が存在する場合、エラーメッセージを表示することなく、行います。I. e. grep foo xyz | grep bar xyz | wc で何行目かを知ることができます。 xyz には bar の両方を含む行の数を期待しているのに対して foo . パイプラインのコマンドを使用する前に引数を変更しなければならないのは、エラーが発生しやすいものです。それに加えて、サイレントエラーの可能性もあり、特に陰湿な行為となります。

前者の理由も重要で、多くの " 良い味 "。 というのは、上記の無言の失敗のように、教育を必要とする人が「でも、あの猫って役に立たないよね」と言った瞬間に思いつかないような、直感的な潜在意識による理由付けに過ぎないのです。

しかし、前者の「センスがいい」という理由も意識してみることにします。その理由は、Unix の直交設計の精神に関係しています。 bargrepcutls . したがって、少なくとも grep は設計精神に反しています。直交するやり方は grep foo file1 file2 file3 . では cat file1 file2 file3 | grep foo の特殊なケースに過ぎません。 grep foo file1 であり、もしあなたがそれを同じように扱わないのであれば、少なくともあなたは無駄な猫賞を避けようとして脳のクロックサイクルを使っていることになります。

という議論につながります。 grep foo file1 file2 file3 は連結であり grep foo file1 file2 file3 は連結しているので、適切なのは cat が適切ですが cat file1 file2 file3 が連結されていないため cat の両方の精神に違反していることになります。 cat file1 | grep foo と全能のUnixの両方の精神に違反することになります。もしそうなら、Unix は一つのファイルの出力を読んで標準出力に吐き出すために別のコマンドを必要とするでしょう(ページ分割や何かではなく、純粋に標準出力に吐き出すだけです)。つまり、次のような状況になるわけです。 cat または、次のように言います。 cat file1 file2 を避け、意識的に dog file1 を避けながら、賞をもらうために cat file1 のデザインがうまくいけばいいのですから。 dog file1 file2 は複数のファイルが指定された場合、エラーを投げるからです。

うまくいけば、この時点で、ファイルを標準出力に吐き出す個別のコマンドを含めない Unix 設計者に共感していただけると思いますが、同時に dog を他の名前をつけるのではなく concatenate と名づけたことです。 cat の不正確なコメントを削除しました。 <edit> は、実際には < はパイプラインの最初に位置する標準出力にファイルを吐き出すための効率的なノーコピー機能なので、Unixの設計者はこのために特別なものを含めました。 <

次の質問は、単にファイルやいくつかのファイルの連結を標準出力に吐き出すだけで、それ以上の処理を行わないコマンドを持つことがなぜ重要なのでしょうか。1 つの理由は、標準入力で動作するすべての Unix コマンドが、少なくとも 1 つのコマンド ライン ファイルの引数を解析する方法を知っていて、それが存在する場合は入力として使用することを避けるためです。もう一つの理由は、ユーザが以下のことを覚える必要がないようにするためです。(a) ファイル名の引数がどこに行くのか、(b) 前述のサイレント パイプライン バグを回避するためです。

そこで、なぜ </edit> が余分なロジックを持つ理由です。その理由は、頻繁に使用されるコマンドのために、ユーザーの流動性を確保するためです。 スタンドアロン ベースで (パイプラインとしてではなく) 頻繁に使用されるコマンドについて、ユーザーの流動性を確保するためです。これは、使いやすさを大幅に向上させるために、直交性をわずかに妥協したものです。すべてのコマンドがこのように設計されるべきではありませんし、頻繁に使用されないコマンドは、ファイル引数の余分なロジックを完全に避けるべきです(余分なロジックは不必要な脆弱性(バグの可能性)につながることを覚えておいてください)。例外として、ファイル引数を許可する場合は grep . (ちなみに grep はファイル引数を受け入れるだけでなく、かなり必要とする全く別の理由があることに注意してください)

最後に、より良くできたこととして、もし ls (ただし、必ずしも grep ) は、ファイル引数が指定されたときに標準入力も利用可能であれば、エラーを発生させます。