Linuxでファイルを探すときに使うコマンド（メモ）

これは何？
========

ファイルを探すときに使えるコマンドの一覧記事です。何番煎じかってはなしですが、メモですので。

やりたかったことはjava(openjdk)のソースコードから、publicなinterfaceを探すことです。コマンドは以下。

```bash
find . -type f -name "*.java" | xargs grep "public interface " | grep -E "\./.+/(.+)\.java:public interface \1"
```

一応解説すると、

* find: 「.」ディレクトリ以下から"*.java"というファイルを抽出する
* xargs: findで得られた標準出力をコマンドの引数として渡す
    * grep: "public interface "を含む行を与えられたファイル名のファイルを開いて探す
* grep: 得られた"public interface "を含むファイル一覧から"./ディレクトリツリー/クラス名.java:public interface クラス名"を含む行を探す

例えばxargsのパイプまで実行すると
`./org/xml/sax/Locator.java:public interface Locator {`
といった行が得られるので、これをgrepする。

コマンドの仕様
============
参考: [ITPro](http://itpro.nikkeibp.co.jp)

`ls [オプション] [対象ディレクトリ]`: ファイル一覧
`$pwd`: カレントディレクトリを示す環境変数。`echo $pwd`は`pwd`と同じ。
`find [オプション] 検索対象ディレクトリ 条件`: 条件にそうファイル一覧
`sed [オプション] 正規表現`: 文字列の置換
`xargs [オプション] コマンド`: 入力各行をコマンドへ渡す
`grep [オプション] [-e] 正規表現 [-f] ファイル名`: マッチする行を抽出
`which コマンド名`: コマンドがあるディレクトリを表示。大体は`/usr/bin/コマンド名`という出力になるはず。

この記事ではスルーするーものー
---------------------------
`awk`に関しては、使い方がわかりません。もっぱら`ruby`か`perl`使いますし、この記事ではスルーです。
`ruby`や`perl`では大げさなときでも`vi`の使い方がわからないので`nano`を使ってます。しかしnanoも正規表現マッチングによる文字列の置換が行える一方で、stdin/outが使えないので今回はスルーです。
一応`vi`系の擁護をしておくと`vim`ではstdinを読み込めるようです（`ls -l | vim -`）。`vipe`があれば、stdoutへ吐くこともできます。ただし使ったことがありません。

コマンドによる正規表現の違い
========================

* grep: 基準。
* sed: `+`が使えない。メタ文字が異なる。
* ls, find: 正規表現は使わないが、シェルのワイルドカードとして`*`がある。`/**/`というワイルドカード?もある。
    * つまり、`ls **/io`とかするとサブディレクトリの`io/`がすべてlsされる。
    * `ls **/*.java`などはできないようだ。
    * `ls *.java`とすればjavaファイルのみが一覧されるのは常識。

ls
---

lsというよりシェルのワイルドカードについて。

* `*`: 任意の0文字以上の文字列
* `?`: 任意の1文字
* `[]`: []内の任意の1文字。`[0-9]`で任意の数字1文字
* `{}`: {}内の文字列を用いたすべての組み合わせ。`{org,sun,java}w`でorgw sunw javawと展開
    * `{org,sun,java}[xw]`はorg[xw] sun[xw] java[xw]と展開される。
    * `{org,sun,java}{x,w}`はorgx orgw sunx sunw javax javawと展開される。
    * `{org,sun,java}{,w}`はorg orgw sun sunw java javawと展開される。

ワイルドカードの`{}`を使うと存在しないディレクトリに対しても文字列展開されるため、

```bash
ls: orgw にアクセスできません: そのようなファイルやディレクトリはありません
```
のようなエラーが出る。

sed
---

具体的には、`*`は`\{0,\}`と同じ意味です。`+`はないので`\{1.\}`か`*`で代用します（BRE）。
文字列のキャプチャには\(〜\)を使います。

grep
----

`-E`をつけたり`egrep`を代わりに使ったりすれば標準の`grep`ではだいたいテキストエディタのものと似たような感じに（ERE）、`-G`をつけると`sed`と同じになります（BRE）。

テキストエディタのものと同じ感じっていうのは例えばabba, civicなどに対して(\w)(\w)\w?\2\1のように書けばマッチするとかそういう感じのもの。\wよりPOSIX準拠するなら[[:alpha:]]とかを使ったほうがいいのかも？

ファイルを探すときはプログラミングするとき違ってファイル名の一部にマッチすればいいので、grep1発で終わるような表現考えるくらいならfixed stringの`grep`（`fgrep`）を多段で使ったほうが頭がこんがらがらないでいいんですけどね。

よく使うオプション
===============
0. `ls`
    * `-l`: ファイルの詳細をリスト表示。パイプで何かしようとしてる今回の要求にはそぐいません。
    * `-a`: ドットファイル（隠しファイル）の表示
    * `-F`: ディレクトリ名の後ろに`/`がついたりします。
    * `-f`: `-aU`の別名。`-F`と混同しないように書いておきます。
1. `find`
    * `-name ファイル名`: ファイル名で検索
    * `-exec コマンド`: (1ファイルずつ?)見つけたファイルへコマンドを実行（末尾につける）、ファイル名はコマンドに{}という文字を入れるとファイル名へ置換される
2. `xargs`
    * `xargs rm`で該当ファイルが削除されるはずです。多分。
    * `xargs mv -t 移動先ディレクトリ`で該当ファイルが移動するはずです。多分。
3. `grep`
    * `grep 正規表現`: 標準出力から正規表現にマッチした行を出力。
    * `grep 正規表現 ファイル名`: ファイルから正規表現にマッチした行を出力。
    * `-e`: 拡張正規表現。"{"のマッチに\{ではなく[{]を使うことが推奨されていたりといろいろ面倒。コマンド`egrep`と同じ。
    * `-F`: 文字列での検索。正規表現を使わないので"."などをエスケープする手間が減る。
    * `-f`: ファイル名の指定。-Fと間違いやすいので記述した。あまり使わない。
    * `-n`: マッチした行番号を表示。`ディレクトリ名:行番号:マッチした位置`と表示されるようになる。
    * `--color=auto`: パイプライン処理すると色が消えるので付けたい場合は指定。自分はちょっと邪魔に感じるので`alias`するまではないかな、と思います。
    * `-r`: 指定したディレクトリから再帰的に探索。`-d recurse`の別名。
    * `-R`: シンボリックリンクを含めて再帰的に探索。Ubuntuでは`-r`と動きが違うようです。
4. `sed`
    * `sed s/検索/置換/g`が基本形。
    * 後方参照したいときは`\(〜\)`で囲んでキャプチャ、`\1`, `\2`, ... で参照します。
    * `-e 条件式` 明示的に条件式を書く場合
    * 文字列の挿入もできるようです。ファイル検索には使わないのでスルー。

それで？
=======

これは何？でやりたいことはできました。sedを使ったりと試行錯誤しましたが全く無意味でしたって話です。`which`はファイル検索よりも`update-alternatives`で使うほうが多いかもしれません。

追記
----

1\. [Qiita上の既存の記事](http://qiita.com/mochizukikotaro/items/a04c03a0d0a33848fcf2)に、grepのあとで/dev/nullをつけておくと必ずディレクトリ名が併記されて便利と書かれていました。

```bash
find . -name "*.java" | xargs grep "public interface " /dev/null
```
ってことですか？

2\. そして、[また他の記事](http://qiita.com/fieldville/items/58aa806d6e566f42d158)で、

```bash
find . -name "*.java" | xargs grep "public interface " /dev/null
```
は

```bash
grep -lr "public interface"
```
でも似たような動きになると判明。ただどの文に引っかかったのかはわからないようなのでここからパイプライン処理するのは難しそうな気がします。。。

3\. 「よく使うオプション」へ`-n`オプション追加しました。

```bash
grep -lrn "public interface"
```
としてみたもののうまく行かなかったので、行番号がほしい時はxargsを使用する必要があるようです。


```bash
find . -name "*.java" | xargs grep --color=auto -n "public interface " /dev/null
```

3.5\. `-l`オプションがファイル名のみの表示にしていたようで、これを取り外せばxargsを用いたものと同じ出力が得られました。

```bash
grep -rn "public interface"
```

4\. ITProさんの記事から[ワイルドカード一覧](http://itpro.nikkeibp.co.jp/article/COLUMN/20060228/231064/)を輸入。

5\. manから、`grep` `-r`と`-R`の意味を追記しました。`-r`はシンボリックリンクがコマンドライン上にあるときだけたどると書いてあるが、意味がわからない。

6\. grepの標準がBREだったことに気づいてERE基準になってる記述の一部を修正。