コマンドライン:grepに一致するすべてのファイル名を検索して置換


80

grepに一致するすべてのファイルの文字列を検索して置換しようとしています:

grep -n 'foo' * 次の形式で出力が表示されます。

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

grepから返されたファイルごとにfoobar。に置き換えてファイルを変更したいと思います。

回答:


70

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

3
スペースを含むファイル/フォルダで問題が発生する可能性があると思います。Can't open Untitled: No such file or directory, <> line 5「無題のフォルダ/file.txt」を試すとき。
Xeoncross 2011

108

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

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

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

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

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


1
cygwinユーザーへの警告の言葉。findとsedの組み合わせは、ストリーミングされるファイルのユーザー権限を変更するようです。これは、find / sedが操作されたときに使用されたのと同じdirレベルからコマンドchmod-R 644 *を使用することで簡単に修正できます。
kaskelotti 2013

-i引数を使用したくない人への警告の言葉:使用しないと機能しません(理由は聞かないでください)
knocte 2013

4
@knocte -iは、ファイルを変更するようにsedに指示します。それ以外の場合は、変更されたバージョンをstdoutに出力するだけです。.bakファイルを作成したくない場合は、「。bak」の部分を省略してください。-iはスタンドアロンでも機能します。
MattJ 2013

2
OSXでは、findコマンドに開始するディレクトリを指定する必要があります。たとえば、find . -name '*.html'またはfind directoryname/ -name '*'
Michiel Kauw-A-Tjoe 2014年

-eを追加する必要がありました。そうしないと、「s ...」の部分が接尾辞であると考えられましたsed -ie 's/foo/bar/g' *
vish 2014

14

あなたがいる場合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/' *

2
for i in *; do ...冗長であるため、sedはファイルのリストを引数として取ることができます。
イェンス2012年

2
@Jensは、上記の例に独自の例を追加して、この回答を改善してみませんか?
マグパイ2014年

変数は常に引用する必要がありますfor i in *; do; sed -i 's/foo/bar/g' "$i"; done
cat

6

私は上記のソリューションまたはシステム全体の検索を気に入って使用し、何千ものファイル間で置き換えました。

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

'* .htm?'で仮定します .htmlの代わりに、.htmファイルと.htmlファイルを同様に検索して検索します。

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


4

これは、perlやfindを使用せずにgrepを使用して機能します。

grep -rli 'old-word' * | xargs -i@ sed -i 's/old-word/new-word/g' @

xargsにはOSXまたはBSDで-iがありませんopenbsd.org/cgi-bin/man.cgi/OpenBSD-current/man1/…大文字の「I」を使用するつもりでしたか?
トニーアダムス

-i他のOSでは動作しないとは知りませんでした。私のためにubuntuで動作します。
pymarco 2014年

私のxargsマンページ(Ubuntu)には、これ-iは非推奨であり、-I代わりに使用すると書かれています。そこで、我々は言う必要があります:grep -rli 'old-word' * | xargs -I filepath sed -i 's/old-word/new-word/g' filepath
マックス・ウォレス

3

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


@knocte、 "cmd"は、任意のツールを選択した場合の検索および置換コマンド全体のトークンです。この回答は、空白に含まれるファイル名をどのように処理するかについての質問に答えます。
トニーアダムス

1

findsedを使用することについてすでに与えられた答え

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

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

ほとんどの簡単な使用法では、コマンド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

これは実際には見た目よりも簡単です。

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

簡単なピーシー。find、grep、xargs、sed、awkをよく理解していれば、bashでのテキストファイルの操作に関してはほとんど不可能なことはありません:)


ああ、無視して、このピマルコはすでにこれを上でカバーしました。説明のために私の答えを残します。
Siliconrockstar 2015年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.