bashやzshellなどのシェルを使用して、再帰的な「検索と置換」を行うにはどうすればよいですか?言い換えると、このディレクトリとそのサブディレクトリ内のすべてのファイルで、すべての「foo」を「bar」に置き換える必要があります。
bashやzshellなどのシェルを使用して、再帰的な「検索と置換」を行うにはどうすればよいですか?言い換えると、このディレクトリとそのサブディレクトリ内のすべてのファイルで、すべての「foo」を「bar」に置き換える必要があります。
回答:
このコマンドで実行できます(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'
仕組みは次のとおりです。
find . -type f -name '*.txt'
現在のディレクトリ(.
)以下で、-type f
名前がで終わるすべての通常ファイル()を検索します.txt
|
そのコマンドの出力(ファイル名のリスト)を次のコマンドに渡しますxargs
これらのファイル名を収集し、それらを1つずつ渡します sed
sed -i '' -e 's/foo/bar/g'
「バックアップなしで所定の場所でファイルを編集し、次の置換(s/foo/bar
)を行ごとに複数回行う(/g
)」(を参照man sed
)行4の「バックアップなし」の部分は私にとっては問題ないことに注意してください。変更するファイルはいずれにせよバージョン管理下にあるので、間違いがあった場合は簡単に元に戻すことができます。
-print0
オプションなしでパイプ検索結果をxargsに決してパイプしないでください。名前にスペースなどが含まれているファイルでは、コマンドは失敗します。
find -name '*.txt' -exec sed -i 's/foo/bar/g' {} +
GNU findでこれらすべてを行うだけです。
sed: can't read : No such file or directory
私が実行したときにfind . -name '*.md' -print0 | xargs -0 sed -i '' -e 's/ä/ä/g'
、しかし、find . -name '*.md' -print0
多くのファイルのリストを与えます。
-i
との間のスペースを削除すると機能します''
''
の後にsed -i
何''
の役割は?
Gitを使用している場合、これを行うことができます。
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
ファイル名のみをリストします。-z
各結果の後にヌルバイトを出力します。
プロジェクトの一部のファイルにはファイルの最後に改行がないため、これを行うことになり、sedは他の変更を加えない場合でも改行を追加しました。(ファイルの最後に改行が必要かどうかについてのコメントはありません。🙂)
find ... -print0 | xargs -0 sed ...
ソリューションは、はるかに時間がかかるだけでなく、まだ存在しないファイルに改行を追加します。これは、gitリポジトリ内で作業する際の迷惑です。git grep
それに比べて速く点灯しています。
これは私がこれに使用する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
(例えば)
試してください:
sed -i 's/foo/bar/g' $(find . -type f)
Ubuntu 12.04でテスト済み。
サブディレクトリ名やファイル名にスペースが含まれる場合、このコマンドは機能しませんが、サブディレクトリ名がある場合は機能しないため、このコマンドは使用しないでください。
通常、ディレクトリ名とファイル名にスペースを使用することは悪い習慣です。
http://linuxcommand.org/lc3_lts0020.php
「ファイル名に関する重要な事実」をご覧ください
現在、このシェルスクリプトを使用します。これは、他の回答とWebの検索から学んだことを組み合わせたものです。私はと呼ばれるファイルの中に置かれchange
、私の上のフォルダに$PATH
していましたchmod +x change
。
#!/bin/bash
function err_echo {
>&2 echo "$1"
}
function usage {
err_echo "usage:"
err_echo ' change old new foo.txt'
err_echo ' change old new foo.txt *.html'
err_echo ' change old new **\*.txt'
exit 1
}
[ $# -eq 0 ] && err_echo "No args given" && usage
old_val=$1
shift
new_val=$1
shift
files=$* # the rest of the arguments
[ -z "$old_val" ] && err_echo "No old value given" && usage
[ -z "$new_val" ] && err_echo "No new value given" && usage
[ -z "$files" ] && err_echo "No filenames given" && usage
for file in $files; do
sed -i '' -e "s/$old_val/$new_val/g" $file
done
次のコマンドはUbuntuおよびCentOSで正常に機能しました。ただし、OS XIではエラーが発生し続けました。
find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;
sed:1: "./Root":無効なコマンドコード。
xargsを介してparamsを渡そうとしたときに、エラーなしで正常に機能しました。
find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'
-i
には、-i ''
おそらくあなたが変わったという事実よりも関連性がある-exec
に-print0 | xargs -0
。ところで、おそらくあなたは必要ありません-e
。
# Recursively find and replace in files
find . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'
上記は魅力のように機能しましたが、リンクされたディレクトリでは、-L
フラグを追加する必要があります。最終バージョンは次のようになります。
# Recursively find and replace in files
find -L . -type f -name "*.txt" -print0 | xargs -0 sed -i '' -e 's/foo/bar/g'