Как заменить строку в файле?

Замена строк в файлах на основе определенных критериев поиска – очень общая задача. Как я могу

  • заменить строку foo на bar во всех файлах в текущем каталоге?
  • сделать то же самое рекурсивно для подкаталогов?
  • заменить, только если имя файла совпадает с другой строкой?
  • заменить, только если строка найдена в определенном контексте?
  • заменить, если строка находится на определенном номере строки?
  • замените несколько строк той же заменой
  • заменить несколько строк различными заменами

6 Solutions collect form web for “Как заменить строку в файле?”

1. Замена всех вхождений одной строки на другую во всех файлах в текущем каталоге:

Это касается случаев, когда вы знаете, что каталог содержит только обычные файлы и что вы хотите обрабатывать все не скрытые файлы. Если это не так, используйте подходы в 2.

Все sed решения в этом ответе предполагают GNU sed . Если вы используете FreeBSD или OS / X, замените -i на -i '' . Также обратите внимание, что использование ключа -i с любой версией sed имеет определенные последствия для безопасности файловой системы и нецелесообразно в любом скрипте, который вы планируете распространять каким-либо образом.

  • Не рекурсивные файлы в этом каталоге:

     sed -i -- 's/foo/bar/g' * perl -i -pe 's/foo/bar/g' ./* 

    ( perl не будет работать для имен файлов, заканчивающихся на или пробел) ).

  • Рекурсивные, обычные файлы ( включая скрытые ) в этом и во всех подкаталогах

     find . -type f -exec sed -i 's/foo/bar/g' {} + 

    Если вы используете zsh:

     sed -i -- 's/foo/bar/g' **/*(D.) 

    (может быть, если список слишком велик, см. zargs для работы).

    Bash не может проверять напрямую для обычных файлов, необходим цикл (скобки не задают параметры во всем мире):

     ( shopt -s globstar dotglob; for file in **; do if [[ -f $file ]] && [[ -w $file ]]; then sed -i -- 's/foo/bar/g' "$file" fi done ) 

    Файлы выбираются, когда они являются фактическими файлами (-f), и они доступны для записи (-w).

2. Замените, только если имя файла соответствует другой строке / имеет конкретное расширение / имеет определенный тип и т. Д.

  • Нерекурсивные файлы только в этом каталоге:

     sed -i -- 's/foo/bar/g' *baz* ## all files whose name contains baz sed -i -- 's/foo/bar/g' *.baz ## files ending in .baz 
  • Рекурсивные регулярные файлы в этом и во всех подкаталогах

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

    Если вы используете bash (привязки не задают параметры во всем мире):

     ( shopt -s globstar dotglob sed -i -- 's/foo/bar/g' **baz* sed -i -- 's/foo/bar/g' **.baz ) 

    Если вы используете zsh:

     sed -i -- 's/foo/bar/g' **/*baz*(D.) sed -i -- 's/foo/bar/g' **/*.baz(D.) 

    The -- служит для указания sed чтобы в командной строке больше не было флагов. Это полезно для защиты от имен файлов, начиная с - .

  • Если файл имеет определенный тип, например, исполняемый файл (см. man find for more options):

     find . -type f -executable -exec sed -i 's/foo/bar/g' {} + 

    zsh :

     sed -i -- 's/foo/bar/g' **/*(D*) 

3. Замените, только если строка найдена в определенном контексте

  • Замените foo на bar только в том случае, если на одной строке есть baz :

     sed -i 's/foo\(.*baz\)/bar\1/' file 

    В sed использование \( \) сохраняет все, что находится в круглых скобках, и вы можете получить к нему доступ с помощью \1 . Существует много вариантов этой темы, чтобы узнать больше о таких регулярных выражениях, см. Здесь .

  • Замените foo на bar только в том случае, если foo находится в 3d столбце (поле) входного файла (при условии, что поля, разделенные пробелами):

     gawk -i inplace '{gsub(/foo/,"baz",$3); print}' file 

    (требуется gawk 4.1.0 или новее).

  • Для другого поля просто используйте $N где N – номер интересующего поля. Для другого разделителя полей ( : в этом примере) используйте:

     gawk -i inplace -F':' '{gsub(/foo/,"baz",$3);print}' file 

    Другое решение, использующее perl :

     perl -i -ane '$F[2]=~s/foo/baz/g; $" = " "; print "@F\n"' foo 

    ПРИМЕЧАНИЕ. Оба решения awk и perl будут влиять на интервал в файле (удалить ведущие и завершающие пробелы и преобразовать последовательности пробелов в один пробел в тех строках, которые соответствуют). Для другого поля используйте $F[N-1] где N – номер поля, который вы хотите, и для другого использования разделителя полей ( $"=":" устанавливает разделитель выходного поля в:):

     perl -i -F':' -ane '$F[2]=~s/foo/baz/g; $"=":";print "@F"' foo 
  • Заменить foo на bar только на 4-й строке:

     sed -i '4s/foo/bar/g' file gawk -i inplace 'NR==4{gsub(/foo/,"baz")};1' file perl -i -pe 's/foo/bar/g if $.==4' file 

4. Несколько операций замены: заменить на разные строки

  • Вы можете комбинировать команды sed :

     sed -i 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file 

    Имейте в виду, что порядок имеет значение ( sed 's/foo/bar/g; s/bar/baz/g' заменит foo на baz ).

  • или команды Perl

     perl -i -pe 's/foo/bar/g; s/baz/zab/g; s/Alice/Joan/g' file 
  • Если у вас есть большое количество шаблонов, проще сохранить ваши шаблоны и их замены в файле sed скриптов:

     #! /usr/bin/sed -f s/foo/bar/g s/baz/zab/g 
  • Или, если у вас слишком много паттернов для вышеописанного, вы можете прочитать пары паттернов из файла (два разделенных пробелами паттернов, $ pattern и $ replacement в строке):

     while read -r pattern replacement; do sed -i "s/$pattern/$replacement/" file done < patterns.txt 
  • Это будет довольно медленным для длинных списков шаблонов и больших файлов данных, чтобы вы могли прочитать шаблоны и создать вместо них сценарий sed . Следующее предполагает, что разделитель <пробел> разделяет список пар MATCH <space> REPLACE, встречающихся один за строкой в ​​файле patterns.txt :

     sed 's| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|' <patterns.txt | sed -f- ./editfile >outfile 

    Вышеупомянутый формат в значительной степени произволен и, например, не позволяет использовать пробел в MATCH или REPLACE . Метод очень общий: в принципе, если вы можете создать поток вывода, который выглядит как сценарий sed , тогда вы можете передать этот поток как sed скрипт, указав файл сценария sed as-stdin.

  • Вы можете комбинировать и объединять несколько сценариев аналогичным образом:

     SOME_PIPELINE | sed -e'#some expression script' \ -f./script_file -f- \ -e'#more inline expressions' \ ./actual_edit_file >./outfile 

    POSIX sed объединяет все сценарии в один в том порядке, в котором они появляются в командной строке. Ни один из них не должен заканчиваться на \n ewline.

  • grep может работать одинаково:

     sed -e'#generate a pattern list' <in | grep -f- ./grepped_file 
  • При работе с фиксированными строками в качестве шаблонов рекомендуется избегать метасимволов регулярных выражений. Вы можете сделать это довольно легко:

     sed 's/[]$&^*\./[]/\\&/g s| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g| ' <patterns.txt | sed -f- ./editfile >outfile 

5. Множественные операции замены: заменить несколько шаблонов на одну строку

  • Замените любой из foo , bar или baz на foobar

     sed -Ei 's/foo|bar|baz/foobar/g' file 
  • или

     perl -i -pe 's/foo|bar|baz/foobar/g' file 

Хороший инструмент для Linux Linux – это rpl , который был первоначально написан для проекта Debian, поэтому он доступен с apt-get install rpl в любом дистрибутиве Debian и может быть для других, но в остальном вы можете загрузить tar.gz файл в SourgeForge .

Простейший пример использования:

  $ rpl old_string new_string test.txt 

Обратите внимание, что если строка содержит пробелы, она должна быть заключена в кавычки. По умолчанию rpl берет заглавные буквы, но не полные слова , но вы можете изменить эти значения по умолчанию с параметрами -i (игнорировать регистр) и -w (целые слова). Вы также можете указать несколько файлов :

  $ rpl -i -w "old string" "new string" test.txt test2.txt 

Или даже укажите расширения ( -x ) для поиска или даже поиска рекурсивно ( -R ) в каталоге:

  $ rpl -x .html -x .txt -R old_string new_string test* 

Вы также можете искать / заменять в интерактивном режиме с помощью опции -p (prompt):

Вывод показывает количество замененных файлов / строк и тип поиска (регистр в / чувствительные, целые / частичные слова), но он может быть тихим с параметром -q ( тихий режим ) или даже более подробным, перечислением номеров строк которые содержат совпадения каждого файла и каталога с опцией -v ( verbose mode ).

Другие варианты, которые стоит запомнить, – e ( чеки e scapes), которые позволяют regular expressions , поэтому вы можете искать также вкладки ( \t ), новые строки ( \n ) и т. Д. Даже вы можете использовать -f для принудительного разрешения (конечно, только когда у пользователя есть права на запись) и -d для сохранения времени модификации »).

Наконец, если вы не уверены в том, что будет делать точно, используйте -s ( режим имитации ).

Как выполнить поиск и замену нескольких файлов :

Вы также можете использовать find и sed, но я считаю, что эта небольшая строка perl работает хорошо.

 perl -pi -w -e 's/search/replace/g;' *.php 
  • -e означает выполнение следующей строки кода.
  • -i означает редактировать на месте
  • -w писать предупреждения
  • -p петля над входным файлом, печатая каждую строку после того, как скрипт будет применен к ней.

Мои лучшие результаты исходят от использования perl и grep (чтобы убедиться, что файл имеет выражение поиска)

 perl -pi -w -e 's/search/replace/g;' $( grep -rl 'search' ) 

Вы можете использовать Vim в режиме Ex:

заменить строку ALF на BRA во всех файлах в текущем каталоге?

 for CHA in * do ex -sc '%s/ALF/BRA/g' -cx "$CHA" done 

сделать то же самое рекурсивно для подкаталогов?

 find -type f -exec ex -sc '%s/ALF/BRA/g' -cx {} ';' 

заменить, только если имя файла совпадает с другой строкой?

 for CHA in *.txt do ex -sc '%s/ALF/BRA/g' -cx "$CHA" done 

заменить, только если строка найдена в определенном контексте?

 ex -sc 'g/DEL/s/ALF/BRA/g' -cx file 

заменить, если строка находится на определенном номере строки?

 ex -sc '2s/ALF/BRA/g' -cx file 

замените несколько строк той же заменой

 ex -sc '%s/\vALF|ECH/BRA/g' -cx file 

заменить несколько строк различными заменами

 ex -sc '%s/ALF/BRA/g|%s/FOX/GOL/g' -cx file 

Я использовал это:

 grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g' 
  1. Список всех файлов, содержащих old_string .

  2. Замените новую строку в результате пробелами (чтобы список файлов можно было передать в sed .

  3. Запустите sed в этих файлах, чтобы заменить старую строку на новую.

Обновление . Вышеприведенный результат не будет выполняться при именах файлов, содержащих пробелы. Вместо этого используйте:

grep --null -lr "old_string" | xargs --null sed -i 's/old_string/new_string/g'

С точки зрения пользователя, хороший и простой инструмент Unix, который отлично выполняет эту работу, – qsubst . Например,

 % qsubst foo bar *.c *.h 

заменит foo на bar во всех моих файлах C. qsubst особенностью является то, что qsubst выполнит запрос-замену , то есть он покажет мне каждое появление foo и спросит, хочу ли я его заменить или нет. [Вы можете безоговорочно заменить (без запроса) опцией -go , и есть другие варианты, например, -w если вы хотите заменить foo когда это целое слово.]

Как это получить: qsubst был изобретен der Mouse (от McGill) и отправлен в comp.unix.sources 11 (7) в августе 1987. Обновленные версии существуют. Например, версия NetBSD qsubst.c,v 1.8 2004/11/01 компилируется и отлично работает на моем mac.

  • Извлечь конкретную вещь из каждой строки в столбце
  • Добавить слова в список слов с помощью sort -u avoinding duplicata
  • Сложное содержимое переменной не попало в sed
  • как печатать ближайший столбец при поиске определенных строк
  • Как я могу запустить sed для замены строк в файле или делать подобные вещи в PHP?
  • Неоднократно извлекает каждую вторую и третью строки файла с 3-строчными блоками данных
  • «Скобки не сбалансированы», хотя я избежал этого?
  • Сравните два столбца файла
  • Использование sed для вставки латексных команд вокруг заголовков документа
  • Как добавить текст в конец строки, когда шаблон сопоставлен?
  • Почему мои одиночные кавычки исчезают при подстановке с sed
  • Linux и Unix - лучшая ОС в мире.