bash や zshell のようなシェルを使って、再帰的な ‘検索と置換’ を行うにはどうすればいいのでしょうか?言い換えれば、このディレクトリとそのサブディレクトリにあるすべてのファイルの ‘foo’ をすべて ‘bar’ に置き換えたいのです
104 Nathan Long 2012-05-24
このコマンドを実行します(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'
は、カレントディレクトリ (.
) 以下で、名前が.txt
で終わるすべての通常のファイル (-type f
) を見つけます|
は、そのコマンドの出力(ファイル名のリスト)を次のコマンドに渡しますxargs
はそれらのファイル名を集めてsed
に一つずつ渡すsed -i '' -e 's/foo/bar/g'
は、「バックアップなしで、その場で編集して、次のように1行に複数回置換(s/foo/bar
)を行う(/g
)」という意味です(man sed
参照)
4行目の「バックアップなし」の部分は、私にとっては問題ないことに注意してください
これを覚えておかなくてもいいように、私は以下のような対話型のbashスクリプトを使っています
#!/bin/bash
# find_and_replace.sh
echo "Find and replace in current directory!"
echo "File pattern to look for? (eg '*.txt')"
read filepattern
echo "Existing string?"
read existing
echo "Replacement string?"
read replacement
echo "Replacing all occurences of $existing with $replacement in files matching $filepattern"
find . -type f -name $filepattern -print0 | xargs -0 sed -i '' -e "s/$existing/$replacement/g"
131 Nathan Long 2012-05-24
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +
これにより、xargs
依存関係が削除されます
37 Ztyx 2013-01-16
Git を使っている場合は、このようにします
git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'
-l
はファイル名のみをリストアップする。-z
各結果の後にヌルバイトを表示する
結局これをやってしまったのは、プロジェクト内のいくつかのファイルがファイルの最後に改行を入れておらず、他に何も変更を加えていないのにsedが改行を追加してしまったからです。(ファイルの最後に改行を入れるべきかどうかについてはコメントはありません。 🙂)
14 David Winiecki 2016-08-05
これは私が使用している 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
(for example)
4 Brian Agnew 2012-06-20
Try:
sed -i 's/foo/bar/g' $(find . -type f)
Ubuntu 12.04でテストしました
EDIT:
このコマンドは、サブディレクトリ名やファイル名にスペースが含まれている場合には動作しませんが、スペースが含まれている場合には動作しないので、このコマンドは使用しないでください
ディレクトリ名やファイル名にスペースを使うのは一般的に悪い習慣です
ファイル名についての重要な事実」を見る
4 nexayq 2015-11-05
このシェルスクリプトを使用してください
私は今、他の回答から学んだこととウェブ検索から学んだことを組み合わせたこのシェルスクリプトを使用しています。私の$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
2 Nathan Long 2016-02-17
私の場合は、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'
お役に立てれば幸いです
0 neo7 2013-05-22
UbuntuやCentOSでは以下のコマンドで問題なく動作しましたが、OS Xではエラーが出続けていました
find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;
1: “./Root”: 無効なコマンドコード。1: “./Root”: 無効なコマンドコードです
xargs経由でparamsを渡してみたところ、エラーもなく正常に動作しました
find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'
0 John Morgan 2013-09-29
# 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'
0 sMajeed 2019-09-12