web-dev-qa-db-ja.com

Awkまたはsedで文字列を再帰的に検索/置換する方法

次のものをすべて見つけて置換するにはどうすればよいですか。

subdomainA.example.com

subdomainB.example.com

/home/www/ディレクトリツリーの下のすべてのテキストファイルに再帰的に?

598
Tedd

:gitリポジトリを含むフォルダでこのコマンドを実行しないでください。gitを変更すると、gitインデックスが破損する可能性があります。

find /home/www -type f -print0 | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

man findから:

-print0 (GNU findのみ)は、検出されたパス名間の出力区切り文字として空白の代わりにヌル文字(\0)を使用するようにfindに指示します。ファイルに空白や他の特殊文字を含めることができる場合、これはより安全なオプションです。 -print0またはfindを使用する場合は、xargs-exec <command>引数を使用することをお勧めします(xargsには-0引数が必要です)。

779

:gitリポジトリを含むフォルダでこのコマンドを実行しないでください。gitを変更すると、gitインデックスが破損する可能性があります。

find /home/www/ -type f -exec \
    sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

ここでの他の答えと比較すると、これはほとんどの場合より単純であり、Perlの代わりにsedを使用します。これが元の質問が求めていたものです。

226
John Zwinck

私にとって最も簡単な方法は

grep -rl oldtext . | xargs sed -i 's/oldtext/newtext/g'
138
Anatoly

すべてのトリックはほぼ同じですが、私はこれが好きです。

find <mydir> -type f -exec sed -i 's/<string1>/<string2>/g' {} +
  • find <mydir>:ディレクトリを調べます。

  • -type f

    ファイルの種類:通常のファイル

  • -exec command {} +

    この変種の-execは、選択されたファイルに対して指定されたコマンドを実行しますが、コマンドラインは選択された各ファイル名を末尾に追加することによって作成されます。コマンドの総呼び出し数は、一致したファイルの数よりはるかに少なくなります。コマンドラインは、xargsがコマンドラインを構築するのとほぼ同じ方法で構築されています。コマンド内では `{} 'のインスタンスは1つだけ許可されています。コマンドは開始ディレクトリで実行されます。

56
I159
cd /home/www && find . -type f -print0 |
  xargs -0 Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'
39

私にとって覚えておくべき最も簡単な解決策は https://stackoverflow.com/a/2113224/565525 です。

sed -i '' -e 's/subdomainA/subdomainB/g' $(find /home/www/ -type f)

_ note _ -i ''はOSX問題を解決しますsed: 1: "...": invalid command code .

_ note _ :処理するファイルが多すぎる場合はArgument list too longが返されます。回避策 - 上記のfind -execまたはxargsソリューションを使用してください。

28
Robert Lujo

銀の検索者を使用している人のためにag

ag SearchString -l0 | xargs -0 sed -i 's/SearchString/Replacement/g'

Agはデフォルトでgit/hg/svnファイル/フォルダを無視するので、これはリポジトリ内で実行しても安全です。

24
Jacob Wang

再帰的にsedを通過するようにファイルを削減するには、文字列インスタンスをgrepにします。

grep -rl <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g

man grepを実行すると、他の人が丁寧に指摘しているようにgitインデックスの問題を避けて、.gitディレクトリの検索を省略したい場合は--exlude-dir="*.git"フラグを定義することもできます。

あなたを導く:

grep -rl --exclude-dir="*.git" <oldstring> /path/to/folder | xargs sed -i s^<oldstring>^<newstring>^g
15
domdambrogia

エキストラとして1つの素敵なワンライナー。 git grepを使う.

git grep -lz 'subdomainA.example.com' | xargs -0 Perl -i'' -pE "s/subdomainA.example.com/subdomainB.example.com/g"
13
Jimmy Kane

これはgitリポジトリと互換性があり、少し簡単です。

Linux:

git grep -l 'original_text' | xargs sed -i 's/original_text/new_text/g'

マック:

git grep -l 'original_text' | xargs sed -i '' -e 's/original_text/new_text/g'

ありがとう。http://blog.jasonmeridth.com/posts/use-git-grep-to-replace-strings-in-files-in-your-git-repository/

11
seddonym
find /home/www/ -type f -exec Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

find /home/www/ -type fは/ home/www /(およびそのサブディレクトリ)内のすべてのファイルを一覧表示します。 "-exec"フラグは、見つかった各ファイルに対して次のコマンドを実行するようにfindに指示します。

Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g' {} +

ファイルに対して実行されるコマンドです(一度に多数)。 {}はファイル名に置き換えられます。コマンドの最後にある+は、findに多くのファイル名に対して1つのコマンドを作成するように伝えます。

findのmanページによると: "コマンドラインは、xargsがコマンドラインを構築するのとほぼ同じ方法で構築されています。"

したがって、xargs -0または-print0を使用せずに、目標を達成する(およびスペースを含むファイル名を処理する)ことが可能です。

8
unutbu

私はこれを必要としていただけで、利用可能な例の速度には不満でした。それで、私は自分自身を思いつきました:

cd /var/www && ack-grep -l --print0 subdomainA.example.com | xargs -0 Perl -i.bak -pe 's/subdomainA\.example\.com/subdomainB.example.com/g'

Ack-grepは関連ファイルを見つけるのに非常に効率的です。このコマンドは〜145 000ファイルを簡単に交換しましたが、他のユーザーはそんなに時間がかかりましたが、完了するまで待ちきれませんでした。

7
Henno

これを試して:

sed -i 's/subdomainA/subdomainB/g' `grep -ril 'subdomainA' *`
4
RikHic

あなたは以下のようにこれを解決するためにawkを使うことができます、

for file in `find /home/www -type f`
do
   awk '{gsub(/subdomainA.example.com/,"subdomainB.example.com"); print $0;}' $file > ./tempFile && mv ./tempFile $file;
done

これがお役に立てば幸いです。

4
sarath kumar

grep -lr 'subdomainA.example.com' | while read file; do sed -i "s/subdomainA.example.com/subdomainB.example.com/g" "$file"; done

私は、ほとんどの人が何かを「ファイルを読み込んでいる間」にパイプできることを知らないと思いますし、ファイル名の中のスペースを残しながらそれらの厄介な-print0引数を避けます。

Sedの前にechoを追加すると、実際に実行する前にどのファイルが変更されるのかを確認できます。

4
MadMan2064
#!/usr/local/bin/bash -x

find * /home/www -type f | while read files
do

sedtest=$(sed -n '/^/,/$/p' "${files}" | sed -n '/subdomainA/p')

    if [ "${sedtest}" ]
    then
    sed s'/subdomainA/subdomainB/'g "${files}" > "${files}".tmp
    mv "${files}".tmp "${files}"
    fi

done
3
petrus4

やや古い学校ですが、これはOS Xで動作しました。

いくつかのトリックがあります:

•現在のディレクトリの下にある拡張子.slsを持つファイルのみを編集します。

sedがそれらを「任意の文字」として評価しないようにするため、.はエスケープする必要があります。

,は、通常の/の代わりにsedデリミタとして使用されます。

これはvariableのパスにimportを渡すようにJinjaテンプレートを編集することにも注意してください(ただし、これはトピック外です)

まず、あなたのsedコマンドがあなたが望むことをしていることを確認してください(これはstdoutへの変更を表示するだけで、ファイルは変更しません):

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

変更する準備が整ったら、必要に応じてsedコマンドを編集します。

for file in $(find . -name *.sls -type f); do echo -e "\n$file: "; sed -i '' 's,foo\.bar,foo/bar/\"+baz+\"/,g' $file; done

sed コマンドの-i ''に注意してください。私はオリジナルファイルのバックアップを作成したくありませんでした( OS Xでsedを使ったインプレース編集 またはRobert Lujoのコメント)ページ)。

幸せなsedingの人々!

2
Raphvanns

vimまたはgrepツールと一緒にfindを使用しても構わない場合は、このリンクでユーザーGertの回答をフォローアップできます - > 大きなフォルダ階層でテキスト置換を実行する方法

これが契約です:

  • 特定のパスで置き換えたい文字列を再帰的にgrepし、一致するファイルの完全なパスのみを取ります。 (それが$(grep 'string' 'pathname' -Rl)になります。

  • (省略可能)これらのファイルを一元管理されたディレクトリに事前バックアップしたい場合は、これを使用することもできます。cp -iv $(grep 'string' 'pathname' -Rl) 'centralized-directory-pathname'

  • その後、与えられたリンクで提供されているものと同様のスキームに従って、vimの中で自由に編集/置換することができます:

    • :bufdo %s#string#replacement#gc | update
2
mzcl-mn

これは最も一般的であるべきバージョンです。例えばfindは必要ありません(代わりにduを使用)。それはxargsを必要とします、それはPlan 9のいくつかのバージョン(9frontのような)だけで見つけられます。

 du -a | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'

ファイル拡張子のようなフィルタを追加したい場合はgrepを使用してください。

 du -a | grep "\.scala$" | awk -F' '  '{ print $2 }' | xargs sed -i -e 's/subdomainA\.example\.com/subdomainB.example.com/g'
2
bbarker

によると これ ブログ投稿:

find . -type f | xargs Perl -pi -e 's/oldtext/newtext/g;'
2
J.Hpour

私はちょうどトップスを使います:

find . -name '*.[c|cc|cp|cpp|m|mm|h]' -print0 |  xargs -0 tops -verbose  replace "verify_noerr(<b args>)" with "__Verify_noErr(<args>)" \
replace "check(<b args>)" with "__Check(<args>)" 
2
tgunr

また変更を避けるために

  • NearlysubdomainA.example.com
  • subdomainA.example.comp.other

それでも

  • subdomainA.example.com.IsIt.good

(おそらくドメインルートの背後にある考えでは良くない)

find /home/www/ -type f -exec sed -i 's/\bsubdomainA\.example\.com\b/\1subdomainB.example.com\2/g' {} \;
2
NeronLeVelu

複数のファイルを変更する(そしてバックアップを*.bakとして保存する)には:

Perl -p -i -e "s/\|/x/g" *

ディレクトリ内のすべてのファイルを取り、|を「Perlのパイ」と呼ばれるxに置き換えます(パイと同じように簡単です)。

1
Stenemo

IBMi上のQshell(qsh)の場合、OPでタグ付けされたようにbashしないでください。

Qshコマンドの制限事項

  • findに-print0オプションはありません
  • xargsには-0オプションはありません
  • sedに-iオプションはありません

このようにqshの解は:

    PATH='your/path/here'
    SEARCH=\'subdomainA.example.com\'
    REPLACE=\'subdomainB.example.com\'

    for file in $( find ${PATH} -P -type f ); do

            TEMP_FILE=${file}.${RANDOM}.temp_file

            if [ ! -e ${TEMP_FILE} ]; then
                    touch -C 819 ${TEMP_FILE}

                    sed -e 's/'$SEARCH'/'$REPLACE'/g' \
                    < ${file} > ${TEMP_FILE}

                    mv ${TEMP_FILE} ${file}
            fi
    done

警告:

  • 解決策はエラー処理を除外
  • OPのタグのとおり、Bashではありません
1
Perl -p -i -e 's/oldthing/new_thingy/g' `grep -ril oldthing *`
1
Sheena

Gitリポジトリ内のすべての出現箇所を置き換えるには、次のようにします。

git ls-files -z | xargs -0 sed -i 's/subdomainA\.example\.com/subdomainB.example.com/g'

参照してください ローカルのgitリポジトリにファイルをリストしますか? リポジトリ内のすべてのファイルを一覧表示するための他のオプション。 -zオプションは、ファイル名をゼロバイトで区切るようにgitに指示します。これにより、xargs(オプション-0を含む)がスペースや何も含まれていなくてもファイル名を区切ることができます。

1
Perseids

ディレクトリを除外する --exclude-dir=.svn)、および ファイル名にスペースを含める (0Byteとgrep -Zおよびxargs -0の併用)

grep -rlZ oldtext . --exclude-dir=.svn | xargs -0 sed -i 's/oldtext/newtext/g'
1
inetphantom

grepsedの組み合わせ

for pp in $(grep -Rl looking_for_string)
do
    sed -i 's/looking_for_string/something_other/g' "${pp}"
done
1
Pawel

あなたのSVNリポジトリを完全に破壊することなくこれを使いたいのであれば、 'find'に全ての隠しファイルを無視するように指示することができます。

find . \( ! -regex '.*/\..*' \) -type f -print0 | xargs -0 sed -i 's/subdomainA.example.com/subdomainB.example.com/g'
1
Marcus Floyd

あなたがnodeへのアクセス権を持っているなら、あなたはnpm install -g rexreplaceをすることができます

rexreplace 'subdomainA.example.com' 'subdomainB.example.com' /home/www/**/*.*
0
mathiasrw

これは私がOSXとWindows(msys2)のために私が見つけた最良の万能ソリューションです。 sedのGNUバージョンを取得できるものならどれでも動作するはずです。チェックサムを破壊しないように.gitディレクトリをスキップします。

Macでは、最初にcoreutilsをインストールし、gsedがパスにあることを確認してください -

brew install coreutils

それから私は私のzshrc/bashrcにこの関数を貼り付けます - >

replace-recursive() {
    hash gsed 2>/dev/null && local SED_CMD="gsed" || SED_CMD="sed"
    find . -type f -name "*.*" -not -path "*/.git/*" -print0 | xargs -0 $SED_CMD -i "s/$1/$2/g"
}

usage: replace-recursive <find> <replace>
0
cchamberlain

現在のディレクトリおよびサブディレクトリ内の .c および .h ファイルのすべての string_1 に一致するすべての string_2 に置き換えます(.git /を除く)

これは Mac で動作します。

find . -type f -path "*.git*" -Prune -o -name '*\.[ch]' -exec \
sed -i '' -e 's/'$1'/'$2'/g' {} +

これは Linux で動作するはずです(まだテストしていません):

find . -type f -path "*.git*" -Prune -o -name '*\.[ch]' -exec \
sed -i 's/string_1/string_2/g' {} +
0
Klas. S

もっと簡単な方法は、コマンドラインで以下を使用することです。

find /home/www/ -type f|xargs Perl -pi -e 's/subdomainA\.example\.com/subdomainB.example.com/g' 
0
Vijay