コマンドラインから再帰的な検索と置換を行うにはどうすればいいですか?

コマンドラインから再帰的な検索と置換を行うにはどうすればいいですか?

bash や zshell などのシェルを使用して、再帰的な「検索と置換」を行うにはどうすればよいでしょうか。言い換えると、このディレクトリとそのサブディレクトリ内のすべてのファイルで、'foo' のすべての出現を 'bar' に置き換えたいのです。

答え1

このコマンドで実行できます (Mac OS X Lion と Kubuntu Linux の両方でテスト済み)。

# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'

仕組みは次のとおりです:

  1. find . -type f -name '*.txt'現在のディレクトリ(.)以下で、-type f名前がで終わるすべての通常ファイル( )を検索します。.txt
  2. |そのコマンドの出力(ファイル名のリスト)を次のコマンドに渡します。
  3. xargsそれらのファイル名を集めて一つずつ渡すsed
  4. sed -i '' -e 's/foo/bar/g'は、「バックアップなしでファイルをその場で編集し、次の置換 ( s/foo/bar) を 1 行に複数回 ( /g) 実行する」ことを意味します ( を参照man sed)

4 行目の「バックアップなし」の部分は私にとっては問題ありません。変更するファイルはバージョン管理下にあるため、間違いがあった場合は簡単に元に戻すことができます。

これを覚えておく必要がないように、次のように対話型の bash スクリプトを使用します。

#!/bin/bash
# find_and_replace.sh

echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"

find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"

答え2

find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +

これによりxargs依存関係が削除されます。

答え3

Git を使用している場合は、次のようにします。

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'

-lファイル名のみをリストします。-z各結果の後に null バイトを出力します。

最終的にこれを実行したのは、プロジェクト内の一部のファイルのファイル末尾に改行がなく、他に変更を加えていないにもかかわらず sed が改行を追加したためです。(ファイルの末尾に改行が必要かどうかについてはコメントしません。)

答え4

私がこれに使用する zsh/perl 関数は次のとおりです。

change () {
        from=$1 
        shift
        to=$1 
        shift
        for file in $*
        do
                perl -i.bak -p -e "s{$from}{$to}g;" $file
                echo "Changing $from to $to in $file"
        done
}

そして、私はこれを実行します

$ change foo bar **/*.java

(例えば)

関連情報