34216


`grep` コマンド実行時、通常なら対象のファイルは引数で指定しますが、  
`cat` コマンドとパイプを使って渡す例も時々見かけられます。

**grep でファイルを引数として指定する例:**

[code]

    $ grep "PATTERN" FILENAME
    
[/code]

**cat を使ってパイプ経由でファイルを渡す例:**

[code]

    $ cat FILENAME | grep "PATTERN"
    
[/code]

見やすさや実行速度を考えると余計なものを挟まない前者の方が個人的に  
良さそうだと思うのですが、敢えて後者の書き方を選ぶ理由が何かあれば  
教えてください。


CC BY-SA 4.0
2017-04-22T17:02:41.557
34214
2023-06-18T22:03:27.253
2020-08-14T07:59:13.630
3060
3060
question
linux grep
grepコマンドでのファイル指定について


ファイルを検索するだけでしたら、私も前者の書き方の方がスッキリしていて見やすいと思います。

ですが、これはどうでしょう？

[code]

    $ ps | grep "PATTERN"
    
[/code]

このように他のコマンドと組み合わせる場合には標準入力経由が便利です。

また、`grep "PATTERN" FILENAME` の引数の順番を覚えていなくても標準入力を使うのであれば問題なく使えます。

他にもあると思いますが、さっと思いつくのはこんな感じでしょうか。


CC BY-SA 3.0
2017-04-22T19:48:38.527
34215
2017-04-22T19:48:38.527
15705
34214
answer


標準入力にファイルの内容を流すためだけに`cat`を使うのは、1994年頃から ["Useless use of
cat"](https://en.wikipedia.org/wiki/Cat_\(Unix\)#Useless_use_of_cat) (UUoC)
として知られています
([出典とされているメールスレッドのアーカイブ](https://groups.google.com/d/msg/comp.unix.shell/532AcI3-zs4/C76LJv9217QJ))。本家
Stack Overflow
にも「この無駄な`cat`には本当に意味が無いのか？」という趣旨の質問が[あります](https://stackoverflow.com/questions/11710552/useless-
use-of-cat)。この回答ではこれらを参考に利点と欠点を挙げてみます。

## `cat FILENAME | grep "PATTERN"` の利点

  * 処理の流れが読みやすいことがあります。どこがファイル名なのかを考えなくて済みます（たとえば `grep abc def`と書いたときを想像してください）。また `cat a.txt | grep foo | wc -l` のようにパイプによる処理フローが続く場合、処理の流れが左から右に読めます。 
    * この点に関して、リダイレクションを上手く使えば`cat`を使わなくても同様のことができるという[指摘があります](https://stackoverflow.com/questions/11710552/useless-use-of-cat#comment23911950_16619430)が、この回答ではそれには触れないことにいたします。
    * 入出力の挙動が明確になることで、バグを未然に防げることがあります。たとえばfooもbarもある行の数を調べたくて `grep foo a.txt | grep bar a.txt | wc -l` と書いてもこれは「(fooに関係なく) barがある行の数」をエラーメッセージなしに出力します。そうではなく `cat a.txt | grep foo | grep bar | wc -l` か `grep foo a.txt | grep bar | wc -l` と書くと上手くいきます。
  * 複数ファイルを`grep`したくて、かつヒットしたファイルの名前が不要なときは、`cat`を使った方が簡潔に書けます。これについては下の方で詳しく書きます。

## `cat FILENAME | grep "PATTERN"` の欠点

  * ただ1つのファイル名を指定するためだけに`cat`を使うのは無駄です。`grep`に直接ファイル名を指定できます。特に、パイプの無いスタンドアローンなコマンドを重視する場合これは重要です。
  * 大きいサイズのファイルを扱う際、`cat`で読んだ後パイプを通じて`grep`で処理することになるため、無駄が出る場合があります。実際に私の環境でベンチマークをとった結果を下の方に書きました。

## 本当に両者は同じコマンドか？

`grep`に限って言えば、実は `grep "PATTERN" FILENAME` と `cat FILENAME | grep "PATTERN"`
は全く同じ挙動をするコマンドではないです。特にオプション引数を許すなら、`-H`または`--with-filename`をつけたときの挙動が変わります。

[code]

    $ cat a.txt 
    abc
    defg
    hij
    $ grep -H "abc" a.txt 
    a.txt:abc
    $ cat a.txt | grep -H "abc"
    (標準入力):abc
    $ 
    
[/code]

同じようなことが、複数ファイルについて`grep`したいときにも言えます。私の環境では `grep PATTERN FILE1 FILE2 FILE3`
を実行したときにはデフォルトで `-H` がついた挙動になりました。これはマニュアルにも書いてある挙動です。

> -H, --with-filename  
>         Print the file name for each match. This is the default when  
>         there is more than one file to search.  
>  (`man grep`より引用)

ただし、ファイル名表示を抑制する `-h`, `--no-filename`
というオプションもあるのでこれを明示的に指定すれば避けられます。これは入力が1ファイルのときや標準入力しか無いときのデフォルト挙動です。

## ベンチマーク結果

ファイルサイズを変えながら、`grep a FILENAME` と `cat FILENAME | grep a`
の実行速度を計測しました。実験した環境はマルチコアプロセッサ上で動くUbuntuです
(詳細は下の方に書きます)。最初に結果だけ載せると、下のようになりました（両対数プロット）。

[![UUoCがある場合と無い場合の実行時間の両対数プロットです](https://i.stack.imgur.com/UJjwR.png)](https://i.stack.imgur.com/UJjwR.png)

UUoCを使ったコマンドの方が、ファイルサイズ依存のオーバーヘッド分だけ遅いことが分かりました
(両対数プロットであることに注意してください)。以下、この結果について詳しく書きます。

### 実験環境

  * Intel Core i7-7500U CPU @ 2.70 GHz クアッドコア
  * メモリ 16GB
  * ストレージはSSDのみ
  * Ubuntu 16.10 yakkety (x86_64, 4.8.0-46-generic)
  * GNU bash 4.3.46(1)-release (x86_64-pc-linux-gnu)
  * grep (GNU grep) 2.25
  * cat (GNU coreutils) 8.25
  * go version go1.8 linux/amd64

### 実験方法

以下の手順で計測をするプログラムをGoで書いてビルドし、bash上で走らせました。

  1. 1KBから10GBまでの幅で、いくつかの適当なファイルサイズについて調べる。
  2. 指定されたファイルサイズ分ASCIIの'0'で埋められたファイルを用意する。ただし、1KBごとに改行を入れました。つまり、1023バイト'0'が続いた後、'\n'が入るようなファイルにしました。
  3. 各コマンドの実行時間を1回ずつ測り、それぞれ20回実行して平均を計算する。ただしキャッシュの影響が出にくいように、(2つのコマンドをAとBと書くことにして、)Aを10回計測、Bを10回計測、Bを10回計測、Aを10回計測、の順番で計測しました。

実際のソースコードは[このgist](https://gist.github.com/nekketsuuu/96d76da2f818ae59fbe2cf6ae499ea85#file-
main-go)にあります。

### 注意

  * 20回しか反復していないので、結果の最上位桁くらいしか信用できません。
  * [masmさんのご指摘](https://ja.stackoverflow.com/a/34231/19110)どおり、シングルプロセスだと異なる結果になるかもしれません。
  * このプロットでは「適度に改行があるテキストファイル」を対象にベンチマークをとりました。いくつか他の条件でも試してみたところ、「改行が全くないテキストファイル」や「すべて0x0で埋められたバイナリファイル」だと違う形のグラフになることが分かりました。結果の両対数プロットだけ下に添付しておきます。

[![UUoCがある場合と無い場合の実行時間を一切改行が無いテキストファイルについて調べたときの両対数プロットです](https://i.stack.imgur.com/qFmY9.png)](https://i.stack.imgur.com/qFmY9.png)

[![UUoCがある場合と無い場合の実行時間をバイナリファイルについて調べたときの両対数プロットです](https://i.stack.imgur.com/HLi8C.png)](https://i.stack.imgur.com/HLi8C.png)


CC BY-SA 3.0
2017-04-23T00:50:25.210
34216
2017-04-26T13:39:34.680
2017-05-23T12:38:55.250
-1
19110
34214
answer


UNIXの哲学その9「[全てのプログラムはフィルタとして振る舞うようにせよ](https://ja.wikipedia.org/wiki/UNIX%E5%93%B2%E5%AD%A6#.E3.82.AC.E3.83.B3.E3.82.AB.E3.83.BC.E3.82.BA:_UNIX.E3.81.AE.E5.93.B2.E5.AD.A6)」という考え方に基づいてコマンド処理を書く場合には「ファイルの内容をとりあえず流してパイプに繋げる」`cat
FILENAME |` という書き方はアリだと思います。効率(?)や見た目の冗長性は置いておいて。

パイプライン処理の利点は多々ありますが、コマンド群をパイプを繋げて連鎖させることで **メソッドチェーン的な考え方で処理が書ける**
点が個人的には一番有効だと思います。

また副次的な効果ですが、マッチする正規表現を色々と変えたい場合に`PATTERN`
が右端に位置するため、コマンドラインの再編集時にカーソル移動が少し楽になります。


CC BY-SA 3.0
2017-04-23T15:38:27.367
34230
2017-04-23T15:38:27.367
2391
34214
answer


コメントにあるように、この回答には異論があります。どちらが正しいかの判断は、今これを見ている方にお任せします。  
なお、マルチコアが関係ないのは、コメントにある通りです。

* * *

`cat | grep` の方が高速になる場合もあると思います。  
今の時代、マルチコアな CPU が普通になっており、cat が HDD から読みながら、grep がマッチングを行う、ということが並列に処理できるためです。


CC BY-SA 3.0
2017-04-23T16:47:50.100
34231
2017-04-26T10:58:02.080
2017-04-26T10:58:02.080
5288
5288
34214
answer


ちなみに、ファイルが一つだけだったら、こんな風にも書けますよ。読みやすいかどうかはさておき。

[code]

    $ <FILENAME grep "PATTERN" 
    
[/code]


CC BY-SA 3.0
2017-04-24T05:23:47.823
34237
2017-04-24T05:23:47.823
3061
34214
answer