web-dev-qa-db-ja.com

Linuxコマンドラインのグローバル検索と置換

Linuxマシンでgrepに一致するすべてのファイルの文字列を検索して置換しようとしています。私がやりたいことのいくつかの部分を持っていますが、それらをすべて一緒につなげるのが最善かどうかはわかりません。

grep -n 'foo' *は次の形式で出力します。

[filename]:[line number]:[text]

Grepによって返される各ファイルについて、「foo」を「bar」に置き換えて、結果をファイルに書き戻したいと思います。それを行う良い方法はありますか?たぶん派手なパイプラインですか?

79

Grepに一致するすべてのファイルの文字列を検索して置換するということですか?

Perl -p -i -e 's/oldstring/newstring/g' `grep -ril searchpattern *`

編集

これはかなり人気のある質問のように思えるので、更新すると思いました。

最近はほとんどack-grepより使いやすいため。したがって、上記のコマンドは次のようになります。

Perl -p -i -e 's/old/new/g' `ack -l searchpattern`

ファイル名の空白を処理するには、次を実行します:

ack --print0 -l searchpattern | xargs -0 Perl -p -i -e 's/old/new/g'

ack-grep。検索をHTMLファイルのみに制限するとします。

ack --print0 --html -l searchpattern | xargs -0 Perl -p -i -e 's/old/new/g'

また、空白が問題にならない場合はさらに短くなります。

Perl -p -i -e 's/old/new/g' `ack -l --html searchpattern`
Perl -p -i -e 's/old/new/g' `ack -f --html` # will match all html files
70
armandino

あなたが与えた例に基づいて、これはあなたが望むもののようです:

sed -i 's/foo/bar/g' *

再帰的ではありません(サブディレクトリに降りません)。ツリー全体で選択したファイルを置換するニースソリューションの場合は、findを使用します。

find . -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

*.htmlはファイルが一致しなければならない式、.bak 後に -iは、.bak拡張子(任意の拡張子)を使用して元のファイルのコピーを作成し、sed式の最後にあるgは、sedに1行で複数のコピーを置き換えるように指示します最初のものだけより)。 -printを見つけることは、どのファイルが一致していたかを示すのに便利です。これはすべて、システム上のこれらのツールの正確なバージョンに依存します。

108
MattJ

sed(1)-iオプションがある場合、次のように使用します。

for i in *; do
  sed -i 's/foo/bar/' $i
done

そうでない場合は、使用する言語に応じて、次の方法にいくつかのバリエーションがあります。

Ruby -i.bak -pe 'sub(%r{foo}, 'bar')' *
Perl -pi.bak -e 's/foo/bar/' *
13
Keltia

私は上記のソリューションまたはシステム全体の検索を好み、使用し、数千のファイル間で置き換えました。

find -name '*.htm?' -print -exec sed -i.bak 's/foo/bar/g' {} \;

「* .htm?」 .htmlの代わりに、.htmファイルと.htmlファイルを同様に検索して見つけます。

バックアップファイルのクリーンアップを容易にするために、.bakをよりシステム全体で使用されるチルダ(〜)に置き換えます。

6
hans

これは、Perlを使用したり検索する必要なく、grepを使用して機能します。

grep -rli 'old-Word' * | xargs -i@ sed -i 's/old-Word/new-Word/g' @
4
pymarco

find . -type f -print0 | xargs -0 <sed/Perl/Ruby cmd>は、バッチごとに1つのインタープリターをロードして、複数のスペースに含まれるファイル名を一度に処理します。はるかに高速。

3
koolb

これは実際には見かけよりも簡単です。

grep -Rl 'foo' ./ | xargs -n 1 -I % sh -c "ls %; sed -i 's/foo/bar/g' %";
  • grepはツリーを再帰的に処理し(-R)、現在のディレクトリ(./)からファイル名だけを出力します(-l)
  • xargsにパイプされ、一度に1つずつ処理し(-n 1)、シェルコマンドでプレースホルダーとして%を使用します(-I%)(sh -c)
  • shellコマンドでは、最初にファイル名が出力されます(ls%;)
  • 次に、sedは、インライン操作(-i)、fooのsubstution( 's /')とbar(foo/bar)、ファイル全体(/ g)を実行します(再び、%で表されます)

簡単です。 find、grep、xargs、sed、awkを十分に理解している場合、bashでのテキストファイル操作に関してはほとんど何も不可能です:)

1
siliconrockstar

findおよびsedを使用することについて既に与えられた答え

find -name '*.html' -print -exec sed -i.bak 's/foo/bar/g' {} \;

おそらく標準的な答えです。または、sedコマンドの代わりにPerl -pi -e s/foo/bar/g'を使用できます。

ほとんどのクイックユースでは、コマンドrplのほうが覚えやすい場合があります。以下は、現在のディレクトリ内のすべてのファイルでの置換(foo-> bar)です。

rpl -R foo bar .

ほとんどのLinuxディストリビューションではデフォルトでは使用できませんが、インストールは簡単です(apt-get install rplまたは同様の機能)。

ただし、正規表現と逆置換、またはファイル名の変更と検索と置換を含むより厳しいジョブの場合、私が知っている最も一般的で強力なツールはrepren、小さなPython私がしばらく前に、より厄介な名前変更とリファクタリングのタスクのために書いたスクリプト。あなたがそれを好むかもしれない理由は:

  • ファイルの名前変更とファイル内容の検索と置換をサポートします(ディレクトリ間でのファイルの移動や新しい親ディレクトリの作成を含む)。
  • 検索と置換の実行にコミットする前に、変更を確認してください。
  • 後方置換、単語全体、大文字と小文字を区別しない、大文字と小文字を保持する(foo-> bar、Foo-> Bar、FOO-> BAR)モードの正規表現をサポートします。
  • スワップ(foo-> barおよびbar-> foo)または一意でない置換セット(foo-> bar、f-> x)を含む複数の置換で動作します。

例についてはREADMEを確認してください .

1
jlevy