これは何?
ファイルを探すときに使えるコマンドの一覧記事です。何番煎じかってはなしですが、メモですので。
やりたかったことはjava(openjdk)のソースコードから、publicなinterfaceを探すことです。コマンドは以下。
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
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と展開される。
-
ワイルドカードの{}を使うと存在しないディレクトリに対しても文字列展開されるため、
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)を多段で使ったほうが頭がこんがらがらないでいいんですけどね。
よく使うオプション
-
ls-
-l: ファイルの詳細をリスト表示。パイプで何かしようとしてる今回の要求にはそぐいません。 -
-a: ドットファイル(隠しファイル)の表示 -
-F: ディレクトリ名の後ろに/がついたりします。 -
-f:-aUの別名。-Fと混同しないように書いておきます。
-
-
find-
-name ファイル名: ファイル名で検索 -
-exec コマンド: (1ファイルずつ?)見つけたファイルへコマンドを実行(末尾につける)、ファイル名はコマンドに{}という文字を入れるとファイル名へ置換される
-
-
xargs-
xargs rmで該当ファイルが削除されるはずです。多分。 -
xargs mv -t 移動先ディレクトリで該当ファイルが移動するはずです。多分。
-
-
grep-
grep 正規表現: 標準出力から正規表現にマッチした行を出力。 -
grep 正規表現 ファイル名: ファイルから正規表現にマッチした行を出力。 -
-e: 拡張正規表現。"{"のマッチに{ではなく[{]を使うことが推奨されていたりといろいろ面倒。コマンドegrepと同じ。 -
-F: 文字列での検索。正規表現を使わないので"."などをエスケープする手間が減る。 -
-f: ファイル名の指定。-Fと間違いやすいので記述した。あまり使わない。 -
-n: マッチした行番号を表示。ディレクトリ名:行番号:マッチした位置と表示されるようになる。 -
--color=auto: パイプライン処理すると色が消えるので付けたい場合は指定。自分はちょっと邪魔に感じるのでaliasするまではないかな、と思います。 -
-r: 指定したディレクトリから再帰的に探索。-d recurseの別名。 -
-R: シンボリックリンクを含めて再帰的に探索。Ubuntuでは-rと動きが違うようです。
-
-
sed-
sed s/検索/置換/gが基本形。 - 後方参照したいときは
\(〜\)で囲んでキャプチャ、\1,\2, ... で参照します。 -
-e 条件式明示的に条件式を書く場合 - 文字列の挿入もできるようです。ファイル検索には使わないのでスルー。
-
それで?
これは何?でやりたいことはできました。sedを使ったりと試行錯誤しましたが全く無意味でしたって話です。whichはファイル検索よりもupdate-alternativesで使うほうが多いかもしれません。
追記
1. Qiita上の既存の記事に、grepのあとで/dev/nullをつけておくと必ずディレクトリ名が併記されて便利と書かれていました。
find . -name "*.java" | xargs grep "public interface " /dev/null
ってことですか?
2. そして、また他の記事で、
find . -name "*.java" | xargs grep "public interface " /dev/null
は
grep -lr "public interface"
でも似たような動きになると判明。ただどの文に引っかかったのかはわからないようなのでここからパイプライン処理するのは難しそうな気がします。。。
3. 「よく使うオプション」へ-nオプション追加しました。
grep -lrn "public interface"
としてみたもののうまく行かなかったので、行番号がほしい時はxargsを使用する必要があるようです。
find . -name "*.java" | xargs grep --color=auto -n "public interface " /dev/null
3.5. -lオプションがファイル名のみの表示にしていたようで、これを取り外せばxargsを用いたものと同じ出力が得られました。
grep -rn "public interface"
4. ITProさんの記事からワイルドカード一覧を輸入。
5. manから、grep -rと-Rの意味を追記しました。-rはシンボリックリンクがコマンドライン上にあるときだけたどると書いてあるが、意味がわからない。
6. grepの標準がBREだったことに気づいてERE基準になってる記述の一部を修正。