やってみる

アウトプットすべく己を導くためのブログ。その試行錯誤すらたれ流す。

Linuxでgrep置換(ディレクトリ配下すべて再帰的に)

GUIがあれば完璧だった。コマンドは難しくて覚えられず罠がたくさんある。

追記

2017-09-17。すぐ見てコピペできる正確なコマンドが欲しくて。追記以外のコマンドはエラーが出たのがあったので無視すべし。

grep置換。MinPitchClassMinに置換。

find . -type f -name "*.py" -print0 | xargs -0 sed -i.bak 's/MinPitchClass/Min/g'
find . -type f -name "*.py" -print0 | xargs -0 sed -i.bak 's/MaxPitchClass/Max/g'
find . -type f -name "*.bak" -print0 | xargs -0 rm -f

grep検索。database.src.Dataを含むファイルを探す。

$ find . -name "*.py" -print0 | xargs -0 grep 'database.src.Data'

カレントディレクトリ配下以下すべての*.pyファイルを対象とする。(ディレクトリを再帰的に検索するので子、孫、などのディレクトリ配下にあるファイルも対象)

置換前 置換後
MinPitchClass Min
MaxPitchClass Max

主に使うコマンドは以下。

コマンド 概要
find ファイルやディレクトリを見つけてechoする
xargs echoを他のコマンドに渡す
sed ファイルの任意テキスト部分をgrep置換する

findコマンドの引数。

引数 説明
-type f ファイルが対象。ディレクトリ等は対象外。誤爆防止。ディレクトリ=d, シンボリックリンク=l 参考
-name "*.py" 末尾.pyのファイル名が対象。誤爆防止。
-print0 xargsコマンドの-0とセットで使う。ファイルパスに半角スペースがあってもOKにする。

xargsコマンドの引数。

引数 説明
-0 findコマンドの-print0とセットで使う。ファイルパスに半角スペースがあってもOKにする。

sedコマンドの引数。

引数 説明
-i.bak .bakファイル作成
's/MinPitchClass/Min/g' 's/このテキストを/このテキストに置換する/g'

参考

複数のファイル内の文字列をまとめて置換するLinuxコマンド - Qiita
http://bitwalker.dtiblog.com/blog-entry-185.html
http://saba.omnioo.com/note/1015/find%E3%81%A8%E3%81%8Bgrep%E3%81%A8%E3%81%8B%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E5%86%8D%E5%B8%B0%E7%9A%84%E6%A4%9C%E7%B4%A2%E3%81%AA%E3%81%A9/

コマンド

find . -type f | xargs sed -i 's/置換対象文字列/置換後文字列/g'

-i.bakで拡張子.bakのバックアップファイルを残す。

find . -type f | xargs sed -i.bak 's/置換対象文字列/置換後文字列/g'

今回はself.data.accself.data.accountに一括置換したい。

find . -type f | xargs sed -i.bak 's/self.data.acc/self.data.account/g'

拡張子.pyのファイルに限定する。

find . -name '*.py' | xargs sed -i.bak 's/self.data.db_acc[/self.data.db_account[/g'
find . -name '*.py' | xargs sed -i.bak 's/self.data.db_acc./self.data.db_account./g'
find . -name '*.py' | xargs sed -i.bak 's/self.data.db_acc =/self.data.db_account =/g'

メタ文字

qiita.com

sedコマンドのメタ文字は\n(nは数字), &\を表したいなら\\, /を表したいなら\/, &を表したいなら\&と思われる。たぶん。

  • 文脈(コマンド毎に異なる正規表現とメタ文字の記述方法)

問題

対象ファイルが1件もなかった。かわりに*.pyファイルすべてに*.py.bakファイルが生成されてしまった。diffをとっても差分なし。ただのゴミファイル。

差分があるときだけ.bakファイルを作ってくれることを期待していたが、そうではないらしい。

ファイル一括削除

そこで、以下のようにして一括削除した。

find . -type f -name '*.bak' | xargs rm

以下のようにしてもカレントディレクトリ直下のみ削除だった。ディレクトリ配下の該当ファイルすべての削除はしてくれなかった。

rm -r -name *.bak

http://honana.com/memo/%E7%89%B9%E5%AE%9A%E3%81%AE%E6%9D%A1%E4%BB%B6%E3%81%AE%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92%E5%86%8D%E5%B8%B0%E7%9A%84%E3%81%AB%E5%89%8A%E9%99%A4%E3%81%99%E3%82%8B%E6%96%B9%E6%B3%95

ディレクトリ一括削除

複数のリポジトリを結合したため、個別に.gitディレクトリがいくつか残っていた。

$ find . -type d -name .git
./uploader/.git
./database/src/repo/create/.git

ファイル一括削除とは微妙に異なるrmパラメータ。-dディレクトリが空でないと削除できないという謎仕様。

$ find . -type d -name .git | xargs rm -dv
rm: `./uploader/.git' を削除できません: ディレクトリは空ではありません
rm: `./database/src/repo/create/.git' を削除できません: ディレクトリは空ではありません

-r再帰-fで警告なし。これでディレクトリ削除できる。ついでに-vで表示。

$ find . -type d -name .git | xargs sudo rm -rfv
`./uploader/.git/index' を削除しました
...

以下で.gitフォルダがもう存在しないことを確認した。

$ find . -type d -name .git

エラー

find: パス名は評価式の前に置かなければいけません

find: パス名は評価式の前に置かなければいけません

NG...

find . -name *.py

OK!

find . -name "*.py"

ワイルドカードを使うときはダブルクォーテーションで囲わないとエラーになる。

そのようなファイルやディレクトリはありません(No such file or directory)

ファイルパスに半角スペースが含まれていると1つの文字列(パス)として認識してくれない。shell文脈では半角スペースで区切られてしまうから。

【linux】 スペースを含むファイル名を find | xargs で使う方法 at softelメモ

NG...

$ find . -name "*.py" | xargs grep 'database.src.Data'

OK!

$ find . -name "*.py" -print0 | xargs -0 grep 'database.src.Data'

所感

Windowsを使っていたころは秀丸grep置換を使っていた。GUIだったし、正規表現を調べれば簡単だった。

Linuxではいちいちコマンドを調べているから一向に進まない。秀丸環境は最強だった。

秀丸

  • 置換(正規表現), grep検索, grep置換
  • マクロ(罫線引きや文字数カウントなど)
  • 記憶と実行(秀丸上で実行したことを記憶し、そのまま実行できる。ショートカット一発で。)
  • 拡張子別の設定(.pyはTABキー押下でスペースに置き換え、ほかはハードタブなど)

秀丸ファイラ

  • 前回終了状態の復元
  • ファイルパスコピー(ショートカット一発)

Linuxにしてから作業効率がとてつもなく下がっている。Windows文字コード問題から解放されたのは幸せだが、秀丸環境の喪失は痛い。viを使っていないからか。