bash – コマンドラインから再帰的な検索と置換を行うには?

bash find-and-replace shell zsh

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'

その仕組みをご紹介します

  1. find . -type f -name '*.txt' は、カレントディレクトリ (.) 以下で、名前が .txt で終わるすべての通常のファイル (-type f) を見つけます
  2. |は、そのコマンドの出力(ファイル名のリスト)を次のコマンドに渡します
  3. xargsはそれらのファイル名を集めてsedに一つずつ渡す
  4. 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:

このコマンドは、サブディレクトリ名やファイル名にスペースが含まれている場合には動作しませんが、スペースが含まれている場合には動作しないので、このコマンドは使用しないでください

ディレクトリ名やファイル名にスペースを使うのは一般的に悪い習慣です

Learning the shell - Lesson 2: Navigation
Navigating the Linux File System. Introduces directory structure and command line tools for moving around.

ファイル名についての重要な事実」を見る

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_Letterfoo:/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


タイトルとURLをコピーしました