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
はそれらのファイル名を集めてそれらを一つずつsed
に渡しますsed -i '' -e 's/foo/bar/g'
は、「バックアップをせずにその場でファイルを編集し、1行に複数回(s/foo/bar
)を代入する(/g
)」という意味です(man sed
を参照)変更しているファイルはとにかくバージョン管理下にあるので、4行目の「バックアップなし」の部分は私には問題ありません。
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +
これはxargs
依存関係を取り除きます。
もしあなたがGitを使っているならば、あなたはこれを行うことができます:
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
はファイル名のみをリストします。 -z
は各結果の後にヌルバイトを表示します。
プロジェクトの中のいくつかのファイルはファイルの終わりに改行を持っておらず、sedは他の変更をしていなくても改行を追加したので、私はこれをやめました。 (ファイルの最後に改行を入れるべきかどうかについてのコメントはありません。)
これは私がこれに使用する私の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の検索から学んだことを組み合わせたものです。私はそれを私の$PATH
のフォルダーの中のchange
と呼ばれるファイルに入れ、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
私のユースケースは、foo:/Drive_Letter
をfoo:/bar/baz/xyz
に置き換えたいというものでした。私の場合は、次のコードでそれを実行できました。私はファイルの大部分があったのと同じディレクトリの場所にいました。
find . -name "*.library" -print0 | xargs -0 sed -i '' -e 's/foo:\/Drive_Letter:/foo:\/bar\/baz\/xyz/g'
お役に立てば幸いです。
次のコマンドはUbuntuとCentOSではうまくいきました。しかし、OS Xの下で私はエラーを受け続けました:
find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;
sed:1: "./Root":無効なコマンドコード.
私はxargsを介してパラメータを渡してみたとき、それはエラーなしでうまくいきました:
find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'