百日半狂乱

Shut the fuck up and write some code!!

Linuxターミナル、コマンドtips その2: ファイルを見つける(locate, find)、ファイルの中身を調べる(grep)

(2019/06/29追記) 実践的、網羅的かつ簡潔にまとまったドキュメントを見つけたのでメモ(日本語訳もある)*1

github.com

このtipsはこれからLinuxを使っていく必要がある人、特に端末操作に苦戦している人、もしくは端末操作に対して嫌悪感すら抱いている人に向けて書いたものです.作成の経緯はその1の冒頭および注釈に書きました.

前回の話題

linuxターミナル、コマンドtips その1: キーボードショートカットとディレクトリの移動

今回の話題

  • ファイルを見つける(locate, find)
  • ファイルの中身を調べる(grep)

余談:ターミナルを縦に割ることはできないのか? -> tmux

tmuxは、分割可能かつ出し入れ可能なターミナル

縦横にターミナルを割ることも大きな特徴だが、出し、入れ(アタッチ、デタッチ)できることもtmuxの大きな特徴.例えば、ssh先のマシンでプログラムを実行したら、(バックグラウンド実行するなどしないと)処理が終わるまでターミナルを開きっぱなしにしておく必要がある..tmuxを使えば、リモート先でプログラム実行中のtmuxターミナルを一旦デタッチしておいて、後からアタッチして続きを確認するなどといったことが可能となる(もちろんローカルでも同じことができる).

セッション、ウィンドウ、ペインなどの概念があるが、これについては以下のページにわかりやすい図がある.

GNU Screen ライクなターミナルマルチプレクサ「tmux」を使う

各種コマンドについては、例えば以下を参考に.

tmuxの基本コマンド

以下の流れでひと通りの動作を確認

$ sudo apt-get install tmux
$ tmux
C-b % #縦割り
C-b " #横割り
C-b o #ペインの移動
C-b c #ウィンドウ作成
C-b w #ウィンドウ一覧表示、移動
C-b n #次のウィンドウに移動
C-b p #前のウィンドウに移動
C-b d #デタッチ
$ tmux a #アタッチ

余談:TMTOWTDI 「やり方は1つじゃない」-> history | grep 'key word'

インクリメンタルサーチで過去のコマンドを取り出す以外に、historyコマンドに対してgrepするやり方

インクリメンタルサーチ

ctrl+r

ctrl+rを押す度に別の候補を表示

ctrl+r #文字を入力

入力した文字にマッチする過去のコマンドを候補として表示、ctrl+rを押す度に遡る

history | grep 'key word'

例えば、過去に複数回apt-getをやっていて、そのうちの1つをもう一度実行したい場合、

$ history | grep apt-get

該当する履歴の番号(例えば1002だとして)を選んで、

$ !1002

とすれば、該当コマンドを実行できる

ファイルを見つける:locate, find

locateコマンド

locate:ファイルをデータベースから検索する

指定したファイル名のファイルパスをファイルシステム全体から検索して表示する

例えば、stdio.hというファイルがどこにあるのか知りたい時、

$ locate 'stdio.h'
/usr/include/stdio.h
/usr/include/c++/4.6/tr1/stdio.h
/usr/include/c++/4.7/tr1/stdio.h
/usr/include/x86_64-linux-gnu/bits/stdio.h
/usr/lib/perl/5.14.2/CORE/nostdio.h

定期的に更新されるデータベースを探索するので、高速に動作する.どのディレクトリ以下にあるのかも検討がつかない場合に特に重宝する.

sudo updatedb

新しいファイルなどは、データベースに登録されていない可能性があるので、その場合はlocateコマンドを叩く前に以下を実行しておく

$ sudo updatedb

findコマンド

以下の例は基本的に下記URLの焼き直しなので、詳しくは下記URLを参照

find:ファイルを検索する

指定したディレクトリ以下を検索する

$ find /usr

途中で出力を止めたかったら慌てずctrl + c

ディレクトリも含めた全てのファイルのファイルパスが一覧で表示されるはず

カレントディレクトリ以下全体を検索する

find

検索するファイル名を指定する

-nameオプションで、指定したファイル名だけを検索できる

*.cなど、ワイルドカードを使用することもできる

$ find /usr -name "stdio.h"

ファイルのタイプを指定する

-typeオプションを用いれば特定のファイルタイプのみを抽出できる

ファイルだけを検索

$ find /usr -type f

指定がない場合、ディレクトリも出力に含まれる

ディレクトリだけを検索

$ find /usr -type d

例:リストアップした全てのファイルに対してgrepをかける

$ find /usr | xargs grep 'hogehoge'

例えば、カレントディレクトリにあるファイルに対してgrepをかけたい時、

$ find | xargs grep 'hogehoge'

仮にfindで数万個のファイルが見つかったとしても動作する

xargs:引数が多すぎる場合の対処法

例:検索したファイルを全て削除する

$ find hoge-dir/ | xargs rm

ex:srcディレクトリ以下の.oファイルのみを削除する

find src/ -name "*.o" | xargs cp -t /tmp/

例:指定したファイルのみを別のディレクトリにまとめて{コピー|移動}する

ex:srcディレクトリ以下の.ccファイルのみをtmpディレクトリ以下にコピーする

find src/ -name "*.cc" | xargs cp -t /tmp/

ex:移動したい場合

find src/ -name "*.cc" | xargs mv -t /tmp/

例:ソースコードの行数をカウントする

find src include -name "*.cc" -or -name "*.h" | xargs wc -l  

複数の条件を、-or-andで組み合わせることもできる

findまとめ

-typeや-nameで上手に欲しいファイルだけを抽出すれば、xargsとの組み合わせで、可能性が無限大.指定した複数のファイル全てに対して決まった操作を行いたい場合に重宝する.もちろん、rm -rf /hoge-dirディレクトリの中身を全部消すとか、cp -r /src-dir /dist-dirで、ディレクトリまるごとコピーとか、細かいこと気にしない操作で済むのであればそれで構わない.

更に詳しいfindとxargsの組み合わせについて

こちらを参照

grepコマンド

以降の例は基本的に下記URLの焼き直しなので、詳しくは下記URLを参照

grep:指定のパターンにマッチする行を表示する

grep正規表現によって指定したパターンに合致する文字列を含む行を出力する

grepの正規表現まとめ

かなり柔軟なパターンマッチができるという事実だけを覚えておき、必要に応じてググる

指定ファイルのパターンマッチ

$ grep 'hoge' target_file.txt

-e オプションで複数のパターンを一度に検索

$ grep -e 'hoge' -e 'foo' target_file.txt

行数を表示する

$ grep -n 'foo' target_file.txt

大文字小文字を区別しない

$ grep -i 'FOO' target_file.txt

指定文字以外を表示する

$ grep -v 'foo' target_file.txt

例: ソースコードの中から特定の文字列の場所を調べる

例えば、src/以下にある全てのファイルからprintfが書かれている行を抽出したい場合、

$ grep -inr 'printf' src/

-rオプションで、ディレクトリを再帰的に探索

上記はfindとの組み合わせと同じ

$ find src/ -type f | xargs grep -ni 'printf'

例: ソースコードに含まれる文字列の数をカウントする

$ grep 'bar' target_file.txt | wc -l

grepで空白文字のパターンマッチ

[[:blank:]]\sで空白文字

言語やツールによって正規表現の表現方法が異なる

下記サイトによくまとまっている

正規表現メモ

*1:毎日それこそ息するように使っているものから、必要な時に覚えてなくて毎回ググっているものまで、ベタだけどまさに当時これが手元にあればなぁという内容になっていて良くできている。このブログを読んでくれるのも嬉しいけど、何ができて何を調べれば良いかをまず知る目的で一回このドキュメントも一通り読んでおくと、"あれどうやるんだっけなー"などとボヤきつつ当てもなくググる時間を減らせるはず。要チェック。