65

Используя оболочку типа bash или zshell, как я могу сделать рекурсивный поиск и замену? Другими словами, я хочу заменить каждое вхождение 'foo' на 'bar' во всех файлах в этом каталоге и его подкаталогах.

8 ответов8

84

Эта команда сделает это (протестировано на 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' находит в текущем каталоге (.) и ниже все обычные файлы (-type f), имена которых заканчиваются на .txt
  2. | передает результат этой команды (список имен файлов) следующей команде
  3. xargs собирает эти имена и передает их один за другим в sed
  4. sed -i '' -e 's/foo/bar/g' означает «отредактировать файл на месте, без резервной копии, и выполнить следующую подстановку (s/foo/bar) несколько раз в строке (/g)» (смотри man sed)

Обратите внимание, что часть «без резервной копии» в строке 4 - это нормально для меня, потому что файлы, которые я изменяю, в любом случае находятся под контролем версий, поэтому я могу легко отменить, если произошла ошибка.

23
find . -type f -name "*.txt" -exec sed -i'' -e 's/foo/bar/g' {} +

Это удаляет зависимость xargs .

6

Если вы используете Git, вы можете сделать это:

git grep -lz foo | xargs -0 sed -i '' -e 's/foo/bar/g'

-l перечисляет только имена файлов. -z печатает нулевой байт после каждого результата.

Я закончил тем, что сделал это, потому что у некоторых файлов в проекте не было новой строки в конце файла, и sed добавил новую строку, даже когда это не сделало никаких других изменений. (Нет комментариев о том, должны ли файлы иметь перевод строки в конце. )

3

Вот моя функция 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

(например)

2

Пытаться:

sed -i 's/foo/bar/g' $(find . -type f)

Проверено на Ubuntu 12.04.

Эта команда НЕ будет работать, если имена подкаталогов и / или имена файлов содержат пробелы, но если они у вас есть, не используйте эту команду, так как она не будет работать.

Обычно плохая практика - использовать пробелы в именах каталогов и именах файлов.

http://linuxcommand.org/lc3_lts0020.php

Посмотрите на "Важные факты об именах файлов"

1

Используйте этот сценарий оболочки

Теперь я использую этот сценарий оболочки, который объединяет вещи, которые я узнал из других ответов и поиска в Интернете. Я поместил его в файл с именем 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
0

В моем случае я хотел заменить 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

Следующая команда отлично работала на Ubuntu и CentOS; Тем не менее, под OS XI продолжал получать ошибки:

find . -name Root -exec sed -i 's/1.2.3.4\/home/foo.com\/mnt/' {} \;

Сед: 1: "./Root ": неверный код команды.

Когда я пытался передать параметры через xargs, все работало нормально, без ошибок:

find . -name Root -print0 | xargs -0 sed -i '' -e 's/1.2.3.4\/home/foo.com\/mnt/'

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .