Linuxコマンドラインで複数のファイルの文字列を置き換える方法


461

sshサーバーへのアクセスのみで、フォルダー内の多くのファイルの文字列を置き換える必要があります。これどうやってするの?

回答:


602
cd /path/to/your/folder
sed -i 's/foo/bar/g' *

「foo」の出現は「bar」に置き換えられます。

macOSなどのBSDシステムで-i '.bak'は、マンページごとに「リスクの破損または部分的なコンテンツ」などのバックアップ拡張機能を提供する必要があります。

cd /path/to/your/folder
sed -i '.bak' 's/foo/bar/g' *

13
文字列に空白や特殊文字が含まれている場合、これは私にはうまくいかないようです。なぜそうなのか、またはどのようにして脱出する必要があるのでしょうか?ありがとう!
マシューハーブスト2014

13
少なくとも私のバージョンではsed-iその後に古いファイル名に追加される文字列が必要です。したがって、sed -i .bak 's/foo/bar/g' *.xxすべての.xxファイルを同等の.xx.bak名前に移動してから.xx、foo→bar置換を使用してファイルを生成します。
Anaphory 2014年

24
より多くのオプションを探したい場合は、より多くのユースケースをカバーするunixスタック交換に関する回答があります。unix.stackexchange.com/a/112024/13488
Reddy

19
@MatthewHerbstでスペースをエスケープするには、\のように使用しますsed -i 's/foo\ with\ spaces/bar/g' *。私はあなたが久しぶりに気付いたと思います...しかし、このようにして、他の人が同じ問題を見つけられるようにします。
manuelvigarcia 2016

5
再帰的なものについては、以下を試すことができます。ファイルのリストが膨大な場合は機能しないことに注意してください。 sed -i -e 's/foo/bar/g' $(find /home/user/base/dir)
スコット

243

Kasparの回答に似ていますが、gフラグを使用して、行のすべての出現箇所を置き換えます。

find ./ -type f -exec sed -i 's/string1/string2/g' {} \;

大/小文字を区別しない場合:

find ./ -type f -exec sed -i 's/string1/string2/gI' {} \;

23
OSXを使用していて、パターンにドットが含まれている可能性があり、インプレース置換(バックアップファイルなし)が必要な場合は、使用する必要がありますLC_ALL=C find ./ -type f -exec sed -i '' -e 's/proc.priv/priv/g' {} \;この投稿この投稿を参照)
Jonathan H

2
それは「-name「*の.js」でフィルタリングできますので、これは間違いなく、私のために働いた
マルチェロドゥ販売を

1
詳細オプションは便利ですが、変更を加えたかどうかを確認するために再グレップできます。注:ワイルドカードの場合は、「-name "* .php"」を試してください。grepは再帰とワイルドカードに適していません。--include=*.whatever
PJ Brunet

12
チェックアウトしたGitリポジトリのルートからこれを行わないでください。で誤ってデータベースを破損した可能性があります.git/。必ずcd低いレベルに下げてください。
erikprice

1
私はgitリポジトリでこれを実行しただけで、次のようにgit status返されますerror: bad index file sha1 signature.... fatal: index file corrupt。何ができますか?
Jin

165

@kevの答えは適切ですが、直接のディレクトリ内のファイルにのみ影響します。以下の例では、grepを使用してファイルを再帰的に検索しています。それはいつも私のために働きます。

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

コマンドの内訳

grep -r:-- recursive、各ディレクトリの下のすべてのファイルを再帰的に読み取ります。
grep -l:-- print-with-matchesは、一致する行を出力する代わりに、一致する各ファイルの名前を出力します。
grep -i:-- ignore-case

xargs:STDINを引数に変換します。この回答に従ってください。
xargsの-i @〜コマンドが含まれている@〜 :引数のプレースホルダ内の特定の位置で使用する〜コマンド〜、@記号は任意の文字列に置き換えることができプレースホルダです。

sed -i:バックアップなしでファイルを編集します。
sed s / regexp / replacement /regexpに一致する文字列をreplacement置き換えます。
sed s / regexp / replacement / gglobal、最初の一致だけではなく、各一致を置換します。


13
これは私にはうまくいきませんでしたが、これはうまくgrep --include={*.php,*.html,*.js} -rnl './' -e "old-word" | xargs -i@ sed -i 's/old-word/new-word/g' @
Dennis

7
@pymarcoこのコマンドについて説明してもらえますか?なぜsed afterの代わりにxargsを使用しなければならなかったのか|、また、なぜ-ixargsコマンドで使用しなければならないのかわかりません。私はそれが非推奨であり、-I代わりに使用する必要があるマニュアルを読みました。そして@、パターンの開始と終了の区切り文字として使用されていますか?
Edson Horacio Junior

2
問題は特にLinuxに関するものです。
pymarco 2016年

6
OSXでこれはokです:grep -rli 'old-word' * | xargs -I@ sed -i '' 's/2.0.0/latest/g' @
フィリップ・スルタン・

1
いくつかのオプション(rli)と@記号を壊したとしたらいいでしょう
Roymunson '27

37

これは私のために働きました:

find ./ -type f -exec sed -i 's/string1/string2/' {} \;

ハワーバー、これはしませんでした:sed -i 's/string1/string2/g' *。たぶん、 "foo"はstring1、 "bar"はstring2を意味するものではありませんでした。


1
これは、sedではワイルドカード*の扱いが異なるためです。[abc] *は、セット{a、b、c}の任意の数の文字を意味します。[a-z0-9] *はワイルドカード*と同様に機能します。
thepiercingarrow 2016年

8
OSXでの使用:find ./ -type f -exec sed -i '' -e 's/string1/string2/' {} \;
Shaheen Ghiassy 2018

32

これにはすでにリストされているいくつかの標準的な回答があります。一般に、findを使用してファイルを再帰的に一覧表示し、sedまたはperlで操作を実行できます。

ほとんどの場合、rplコマンドの方が覚えやすいでしょう。すべてのファイルに対して再帰的に置換(foo-> bar)します。

rpl -R foo bar .

おそらくそれをインストールする必要があります(apt-get install rplまたは同様のもの)。

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

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

、それを使用しますpip install repren。例については、README確認してください


1
うわー、reprenは素晴らしいです!1,000以上のC ++ヘッダーとソースファイル全体で一致するようにファイルの名前を変更しながら、クラス名、メソッド、変数内の単語の一部を変更するために使用しただけで、1つのコマンドで初めて完全に機能しました。ありがとうございました!
ボブKocisko

29

複数のファイルの文字列を置き換えるには、次を使用できます。

grep -rl string1 somedir/ | xargs sed -i 's/string1/string2/g'

例えば

grep -rl 'windows' ./ | xargs sed -i 's/windows/linux/g'

ソースブログ


20

ファイル内のパスを置き換える(エスケープ文字を避ける)には、次のコマンドを使用できます。

sed -i 's@old_path@new_path@g'

@記号は、後続の文字列ですべての特殊文字を無視する必要があることを意味します。


2
正確に私が他のすべての答えで探していたもの。つまり、パスである文字列を変更する場合など、特殊文字の扱い方。ありがとうございました。他の回答では大きな見落としのように見えました。
inspirednz

19

文字列にスラッシュ(/)が含まれている場合は、区切り文字を「+」に変更できます。

find . -type f -exec sed -i 's+http://example.com+https://example.com+g' {} +

このコマンドは、現在のディレクトリで再帰的に実行されます。


ありがとう!助けた変更の参照の負荷../domain.comdomain.com
ジャック・ロジャース

12

「foo」の最初の行の出現は「bar」に置き換えられます。また、2行目で確認できます。

grep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
grep 'foo' -r * | awk -F: {'print $1'} | sort -n | uniq -c

6
なぜブログにリンクしているのですか?回答とまったく同じテキストが含まれています。
DavidPostill 2017年

1
好きgrep -rl 'foo' . | xargs sed -i 's/foo/bar/g'
プラチ

10

使用できるファイルのリストがある場合

replace "old_string" "new_string" -- file_name1 file_name2 file_name3

使用できるすべてのファイルがある場合

replace "old_string" "new_string" -- *

拡張子付きのファイルのリストがある場合は、使用できます

replace "old_string" "new_string" -- *.extension

2
「 - 」は、少なくとも私のバージョンでは、実際に、ちょうどあるべき「--file」
simpleuser

4
このユーティリティは、MySQLパッケージ内で配布されます。
ドミトリーギンズバーグ2014年

2
正規表現として正規行を引用せずに機能する解決策を高く評価したいのですが。
ドミトリーギンズバーグ2014年

1
これは正常に機能します。たとえば、「。replace "old_string" "new_string" -- *.txt
txt

1
このreplaceユーティリティを入手する場所から追加することをお勧めします。この情報がなければ、この答えは不完全です。
Sitesh 2018

8

「findとsedを使用することもできますが、このperlの小さな行が適切に機能することがわかります。

perl -pi -w -e 's/search/replace/g;' *.php
  • -eは、次のコード行を実行することを意味します。
  • -iはインプレイス編集を意味します
  • -w警告を書き込む
  • -pループ

"(http://www.liamdelahunty.com/tips/linux_search_and_replace_multiple_files.phpから抽出)

私の最良の結果は、perlとgrepを使用して得られます(ファイルに検索式があることを確認するため)

perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' )

8

文字列を検索し、searchそれreplaceを複数のファイル間で置き換える場合、これは私の戦いでテストされた1行の式です。

grep -RiIl 'search' | xargs sed -i 's/search/replace/g'

grepの簡単な説明:

  • -R -再帰検索
  • -i - 大文字小文字を区別しません
  • -I -バイナリファイルをスキップします(テキストが必要ですか?)
  • -l-単純なリストを出力として印刷します。他のコマンドに必要

次に、grep出力は、(xargsを介して)sedにパイプされ、実際にテキストを置き換えるために使用されます。-iフラグは、直接ファイルを変更します。一種の「ドライラン」モードの場合は削除してください。


4

この質問(と回答)が見つかる前に、自分で解決策を練りました。"replace"、 "several"、および "xml"のさまざまな組み合わせを検索しました。これはそれが私のアプリケーションであるためですが、この特定の組み合わせは見つかりませんでした。

私の問題:複雑なオブジェクトを含むテストケースのデータを含むSpring xmlファイルがありました。Javaソースコードのリファクタリングは多くのクラスを変更し、xmlデータファイルには適用されませんでした。テストケースのデータを保存するために、いくつかのディレクトリに分散されたすべてのxmlファイルのすべてのクラス名を変更する必要がありました。元のxmlファイルのバックアップコピーを保存する間(バージョン管理によってここに保存されるため、これは必須ではありませんでしたが)。

find+の組み合わせを探していましたsedていました。他の状況でも機能しましたが、一度にいくつかの置換を行うことはできませんでした。

それから私はubuntuの応答を見つけましたそしてそれは私が私のコマンドラインを構築するのを助けました:

find -name "*.xml" -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;

そして、それは完全に機能しました(まあ、私の場合は6つの異なる置換がありました)。ただし、現在のディレクトリの下にあるすべての* .xmlファイルに触れることに注意してください。そのため、バージョン管理システムに責任がある場合は、最初にフィルタリングして、必要なsed文字列を実際に持っているものにのみ渡すことをお勧めします。お気に入り:

find -name "*.xml" -exec grep -e "firstWord" -e "secondWord" -e "thirdWord" {} \; -exec sed -s --in-place=.bak -e 's/firstWord/newFirstWord/g;s/secondWord/newSecondWord/g;s/thirdWord/newThirdWord/g' {} \;

Windowsの場合、1つのコマンドで文字列を検索する方法があることを確認しました-まだ置換方法を確認していません- findstr /spin /c:"quéquieresbuscar" *.xml 便利です。
manuelvigarcia

3
grep --include={*.php,*.html} -rnl './' -e "old" | xargs -i@ sed -i 's/old/new/g' @

3

本当に不自然ですが、sedコマンドをOSXで正しく機能させることができなかったため、代わりに次のように馬鹿げたことを行いました。

:%s/foo/bar/g
:wn

^-これらの3行をクリップボードにコピーします(はい、最後の改行を含めます)。

vi *

そして、ファイルがなくなったと表示されるまでcommand-vを押し続けます。

ばか...ハック...効果的...



2

ストリームエディターは、-iスイッチで呼び出されたときに「インプレース」で複数のファイルを変更します。スイッチは、引数として終了するバックアップファイルを取ります。そう

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

このフォルダー内のすべてのファイルでに置き換えfooられbarますが、サブフォルダーには移動しません。ただし、これ.bakにより、ディレクトリ内のすべてのファイルに対して新しいファイルが生成されます。このディレクトリとそのすべてのサブディレクトリにあるすべてのファイルに対してこれを再帰的に行うには、などのヘルパーfindがディレクトリツリーをトラバースする必要があります。

find ./ -print0 | xargs -0 sed -i.bak 's/foo/bar/g' *

findfind ./ -name '*.php' -or -name '*.html' -print0必要に応じて、などの引数をさらに指定することにより、変更するファイルをさらに制限できます。


注:GNU sedはファイルの終わりを必要とせずsed -i 's/foo/bar/g' *、同様に機能します。FreeBSD sedは拡張を要求しますが、間にスペースを許可するのでsed -i .bak s/foo/bar/g *機能します。


2

multieditコマンドのスクリプト

multiedit [-n PATTERN] OLDSTRING NEWSTRING

Kasparの回答から、コマンドライン引数を受け入れ、オプションでパターンに一致するファイル名を制限するbashスクリプトを作成しました。$ PATHに保存して実行可能にしてから、上記のコマンドを使用します。

スクリプトは次のとおりです。

#!/bin/bash
_help="\n
Replace OLDSTRING with NEWSTRING recursively starting from current directory\n
multiedit [-n PATTERN] OLDSTRING NEWSTRING\n

[-n PATTERN] option limits to filenames matching PATTERN\n
Note: backslash escape special characters\n
Note: enclose STRINGS with spaces in double quotes\n
Example to limit the edit to python files:\n
multiedit -n \*.py \"OLD STRING\" NEWSTRING\n"

# ensure correct number of arguments, otherwise display help...
if [ $# -lt 2 ] || [ $# -gt 4 ]; then echo -e $_help ; exit ; fi
if [ $1 == "-n" ]; then  # if -n option is given:
        # replace OLDSTRING with NEWSTRING recursively in files matching PATTERN
        find ./ -type f -name "$2" -exec sed -i "s/$3/$4/g" {} \;
else
        # replace OLDSTRING with NEWSTRING recursively in all files
        find ./ -type f -exec sed -i "s/$1/$2/" {} \;
fi

1

ファイルにバックスラッシュ(通常はパス)が含まれている場合は、次のようなことを試すことができます。

sed -i -- 's,<path1>,<path2>,g' *

例:

sed -i -- 's,/foo/bar,/new/foo/bar,g' *.sh (in all shell scripts available)

1

私の個人的な英語ノードを維持するために、ディレクトリの下のすべてのファイルについて、古い文字列と新しい文字列の複数のペアを再帰的に置き換えるのに役立つユーティリティスクリプトを書きました。

古い文字列と新しい文字列の複数のペアは、ハッシュマップで管理されます。

dirはコマンドラインまたは環境変数を介して設定できます。マップはスクリプトでハードコードされていますが、必要に応じてコードを変更してファイルからロードできます。

新機能のため、bash 4.2が必要です。

en_standardize.sh:

#! /bin/bash
# (need bash 4.2+,)
# 
# Standardize phonetic symbol of English.
# 
# format:
#   en_standardize.sh [<dir>]
# 
# params:
# * dir
#   target dir, optional,
#   if not specified then use environment variable "$node_dir_en",
#   if both not provided, then will not execute,
# * 
# 

paramCount=$#

# figure target dir,
if [ $paramCount -ge 1 ]; then # dir specified
    echo -e "dir specified (in command):\n\t$1\n"
    targetDir=$1
elif [[ -v node_dir_en ]]; then # environable set,
    echo -e "dir specified (in environment vairable):\n\t$node_dir_en\n"
    targetDir=$node_dir_en
else # environable not set,
    echo "dir not specified, won't execute"
    exit
fi

# check whether dir exists,
if [ -d $targetDir ]; then
    cd $targetDir
else
    echo -e "invalid dir location:\n\t$targetDir\n"
    exit
fi

# initial map,
declare -A itemMap
itemMap=( ["ɪ"]="i" ["ː"]=":" ["ɜ"]="ə" ["ɒ"]="ɔ" ["ʊ"]="u" ["ɛ"]="e")

# print item maps,
echo 'maps:'
for key in "${!itemMap[@]}"; do
    echo -e "\t$key\t->\t${itemMap[$key]}"
done
echo -e '\n'

# do replace,
for key in "${!itemMap[@]}"; do
    grep -rli "$key" * | xargs -i@ sed -i "s/$key/${itemMap[$key]}/g" @
done

echo -e "\nDone."
exit

1

ackコマンドを使用すると、次のようにかなり高速になります。

ack '25 Essex' -l | xargs sed -i 's/The\ fox \jump/abc 321/g'

また、検索結果に空白がある場合。あなたはそれを脱出する必要があります。


0

Pythonソースの一般的なシバンエラーを修正する例を示します。

grep / sedアプローチを試すことができます。以下は、GNU sedで動作し、gitリポジトリを壊さないものです。

$ grep -rli --exclude '*.git*' '#!/usr/bin/python' . | xargs -I {} \
gsed -i '' -e 's/#!\/usr\/bin\/python/#!\/usr\/bin\/env python/' {}

それとも、使用することができますgreptileを :)

$ greptile -x .py -l -i -g '#!/usr/bin/env python' -r '#!/usr/bin/python' .

最初のスクリプトをテストしたところ、2番目のスクリプトも動作するはずです。エスケープ文字には注意してください。ほとんどの場合、greptileを使用する方が簡単だと思います。もちろん、sedを使用して多くの興味深いことを行うことができます。そのためには、xargsを使用して習得することをお勧めします。


0

私はこれを別の投稿から見つけました(どちらか覚えていません)、最もエレガントではありませんが、シンプルで、初心者のLinuxユーザーとして私に問題を与えていません

for i in *old_str* ; do mv -v "$i" "${i/\old_str/new_str}" ; done

スペースやその他の特殊文字がある場合は、\を使用します

for i in *old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done

サブディレクトリの文字列には**を使用します

for i in *\*old_str\ * ; do mv -v "$i" "${i/\old_str\ /new_str}" ; done

0

以下のコマンドを使用して、最初にファイルを検索し、ファイルを置き換えることができます。

find . | xargs grep 'search string' | sed 's/search string/new string/g'

例えば

find . | xargs grep abc | sed 's/abc/xyz/g'

0

単純なスクリプトファイルを使用することにより、より簡単な方法があります。

   # sudo chmod +x /bin/replace_string_files_present_dir

ファイルをgeditまたは任意のエディターで開きます。ここではgeditを使用します。

   # sudo gedit /bin/replace_string_files_present_dir

次に、エディターで以下をファイルに貼り付けます

   #!/bin/bash
   replace "oldstring" "newstring" -- *
   replace "oldstring1" "newstring2" -- *
   #add as many lines of replace as there are your strings to be replaced for 
   #example here i have two sets of strings to replace which are oldstring and 
   #oldstring1 so I use two replace lines.

ファイルを保存し、geditを閉じます、ターミナルを終了するか、単に閉じてから開始し、追加した新しいスクリプトをロードできるようにします。

編集するファイルが複数あるディレクトリに移動します。次に実行します:

  #replace_string_files_present_dir

Enterキー押すと、これらを含むすべてのファイルのoldstringoldstring1が、それぞれ正しいnewstringnewstring1に自動的に置き換えられます。

古い文字列を含まないすべてのディレクトリとファイルをスキップします。

これは、文字列の置換が必要なファイルを含むディレクトリが複数ある場合に、入力する面倒な作業をなくすのに役立ちます。あなたがしなければならないすべてはそれらのディレクトリのそれぞれにナビゲートし、次に実行することです:

#replace_string_files_present_dir

あなたがしなければならないのは、上で示したように、すべての置換文字列が含まれているか、追加されていることを確認することだけです。

replace "oldstring" "newstring" -- *

ファイル/ bin / replace_string_files_present_dirの最後。

新しい置換文字列を追加するには、ターミナルで次のように入力して作成したスクリプトを開きます。

sudo gedit /bin/replace_string_files_present_dir

追加する置換文字列の数を心配する必要はありません。古い文字列が見つからない場合、効果はありません。


通常、bashで「$ {whatever}の方法」を尋ねると、スクリプトまたはCIジョブの指示(a .gitlab-ci.ymlまたはaなどtravis.yml)にコンパクトバージョンを含めるように求められます。スクリプトを作成して後で実行することで構成されるすべての方向転換は、アンチパターンです。それは、スクリプトの作成をスクリプト化する必要があるためです(そして、私はほとんどの場合面倒になります)
zar3bski

-4

$ replace "From" "To"-`find / path / to / folder -type f`

(replace-MySQLデータベースシステムの文字列置換ユーティリティ)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.