4608


現在、ファイルネームを`find`コマンドで絞り込み、パイプで`xargs
grep`して中身を検索したあとに、さらにその出力結果を、パスで絞り込むために、さらにパイプで`grep`を繋いでいます。しかし、この方法は同じようなコマンドを二度使っているのでちょっと冗長で、もう少しいい方法がないかと考えています。

具体的には以下のようなコマンドです:

[code]

    $ find . -name "*.rb" | xargs grep "HogeHoge" | grep "app"
    
[/code]

`find`の使い方がこなれていないのも、上のように冗長になる要因ではあるかとは思いますが、できることなら`grep`の何らかのオプションで出来る方法があれば教えて下さい。あるいは、この発想自体が筋が悪く、`find`で対象パスを絞り込むべきならば、その方法を教えてください。


CC BY-SA 3.0
2015-01-15T10:14:03.523
4607
2015-01-16T02:13:42.590
2015-01-15T14:19:52.093
33
939
question
linux command-line
findとxargsを利用したgrepでマッチしたファイルをパスで絞り込む方法


※ すっかり勘違いをしていました。

[code]

    $ find . -type f -regex ".*app.*\.rb" | xargs grep 'HogeHoge'
    
[/code]

でしょうか？ 念の為、ディレクトリを grep しない様に `-type f` を find に付けておいた方が良いかと思います。

追記 0:

※ 追記ばかり多くてすみません

`-path`, `-name` オプションと GNU find の `-regex` オプションの違いですが、

[code]

    $ find . -type f -regex ".*app.*\.rb"
      => ./app/a.rb            # Match
      => ./xyz/application.rb  # Match
      => ./application1.rb     # Match
    $ find . -type f -path "*app*/*" -name "*.rb"
      => ./app/a.rb            # Match
      => ./xyz/application.rb  # Not match
      => ./application1.rb     # Not match
    
[/code]

となります。質問者である esehara さんの目的次第になりますが、`-regex` オプションではディレクトリ名でもファイル名でも "app"
という文字列が含まれているファイル全てにマッチしてしまいます。必要に応じて使い分けると良いかと思います。  
なお、`-regex` オプションのない find コマンドで同じ動作をさせたい場合は、以下の様になります。

[code]

    $ find . -type f \( -path "*app*/*" -name "*.rb" \) -o -name "*app*.rb"
    
[/code]

ちなみに、GNU find には `-iregex` というオプションがあって、これは大文字か小文字であるかを無視します(case
insensitive)。同様に `-iname` や `-ilname` オプションも用意されています。

追記 その1:

おそらく、`-regex` は GNU find だけにあるオプションかと思います。

> GNU find(1)
>
> -regex pattern
>
> File name matches regular expression pattern. This is a match on the whole
> path, not a search. For example, to match a file named `./fubar3', you can
> use the regular expression `.*bar.' or `.*b.*3', but not `f.*r3'. The
> regular expressions understood by find are by default Emacs Regular
> Expressions, but this can be changed with the -regextype option.

追記 その2:

fumiyas さんの、

> ファイル名に空白などが含まれていても安全です。

について、xargs を使う場合には、find に `-print0` と xargs に `-0` オプションも付けます。ただし、どちらとも GNU
拡張です。

[code]

    $ find . -type f -regex ".*app.*\.rb" -print0 | xargs -0 grep 'HogeHoge'
    
[/code]

余談ですが、`zero(NULL) option` は GNU tools の幾つかに導入されています。

> GNU grep(1)
>
> -z, --null-data
>
> Treat the input as a set of lines, each terminated by a zero byte (the ASCII
> NUL character) instead of a newline. Like the -Z or --null option, this
> option can be used with commands like sort -z to process arbitrary file
> names.

例えば、この `-ｚ` オプションを使うとテキストファイル全体を単一な論理行とみなして検索を行います。以下は単純なウェブスクレイピングの例です。

[code]

    $ wget -q -O - http://news.tv-asahi.co.jp/news_politics/articles/000041338.html |
      nkf -w |
      grep --binary-files=text --null-data -Po '(?<=<p>)([\s\S]+?\s*)(?=</p>)'
    
[/code]


CC BY-SA 3.0
2015-01-15T10:23:23.867
4608
2015-01-16T02:13:42.590
2020-06-17T08:14:45.997
-1
4607
answer


find の後で絞り込むのが良いと思います。

[code]

    $ find . -name "*.rb" | grep "app" | xargs grep "HogeHoge"
    
[/code]

もしくは grep の検索結果の **ファイル名部分だけ** にマッチさせて

[code]

    $ find . -name "*.rb" | xargs grep "HogeHoge"| grep '^[^:]*app'
    
[/code]

とするのが良いです。(`:` が出て来ない文字が続いて app がマッチします)  
尚、この方法は Windows だとドライブレター`C:\`とマッチする場合もありますのでご注意下さい。


CC BY-SA 3.0
2015-01-15T10:55:15.930
4611
2015-01-15T10:55:15.930
440
4607
answer


`find`(1) は様々な条件で対象ファイルを絞りこむことが可能ですので、一通りマニュアルをご覧になることをお勧めします。条件のほかに条件の AND
(`-a` オプション。省略可能)、OR (`-o` オプション)、グループ化 (`(` と `)` で括る。シェルから実行する場合は `\\(`
のように要エスケープ) も可能です。

「ディレクトリ名に `app` が含まれる `*.rb` ファイルのうち `HogeHoge`
にマッチする行を含むファイルのパス名と該当行を表示する」という要件であれば、`find` や `grep` の実装に依存しない (GNU などに依存しない)
コマンドラインの例は次のようになります。

[code]

    $ find . -type f -path "*app*/*" -name "*.rb" -exec grep "HogeHoge" /dev/null {} +
    
[/code]

このコマンドラインなら GNU などに依存せず、`xargs` も不要で、かつファイル名に空白などが含まれていても安全です。ただし、とても古い OS の
`find` だと `-exec` オプション中の `+` に対応していないものがあるので要注意です。また、`-path`
オプションが対応していないものもあります。

`-path` に対応していない `find` の場合は、ほかの回答のように別途 `grep` などでフィルターする必要があります。

[code]

    $ find . -type f -name "*.rb" |grep "app.*/" |xargs grep "HogeHoge" /dev/null
    
[/code]

これだとパス名に空白文字が含まれている場合にうまく動きませんが、大抵は問題ないでしょう。

`/dev/null` は、条件にマッチするファイルが一つしか存在しなかった場合にも該当ファイルの名前を出力するためのハックです。GNU `grep`
など、`-H` (`\--with-filename`) オプションがない `grep` 実装で有効な方法です。


CC BY-SA 3.0
2015-01-15T13:21:49.303
4624
2015-01-15T14:02:41.193
2015-01-15T14:02:41.193
3061
3061
4607
answer