Я обычно использую di
:
$ di
Filesystem Mount Size Used Avail %Used fs Type
/dev/sda1 / 22.3G 13.1G 9.2G 59% jfs
udev /dev 996.4M 200.0K 996.2M 0% tmpfs
tmpfs /dev/shm 1001.6M 0 1001.6M 0% tmpfs
/dev/sda2 /home 50.2G 32.2G 17.9G 64% jfs
Это для случаев, где Вы знаете, что каталог содержит только регулярные файлы и что Вы хотите обработать все нескрытые файлы. Если это не так используйте подходы в 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).
Нерекурсивный, файлы в этом каталоге только:
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' {} +
При использовании удара (фигурные скобки стараются не устанавливать опции глобально):
( 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.)
--
подачи для сообщения sed
то, что больше флагов не будет дано в командной строке. Это полезно для защиты от имен файлов, запускающихся с -
.
Если файл имеет определенный тип, например, исполняемый файл (см. man find
для большего количества опций):
find . -type f -executable -exec sed -i 's/foo/bar/g' {} +
zsh
:
sed -i -- 's/foo/bar/g' **/*(D*)
Замена foo
с bar
только если существует a baz
позже та же строка:
sed -i 's/foo\(.*baz\)/bar\1/' file
В sed
, использование \( \)
сохраняет то, что находится в круглых скобках, и можно затем получить доступ к нему с \1
. Существует много изменений этой темы, чтобы узнать больше о таких регулярных выражениях, видеть здесь.
Замена foo
с bar
только если foo
найден на 3-м столбце (поле) входного файла (принимающий разделенные от пробела поля):
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
Можно объединиться 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
Если у Вас есть большое количество шаблонов, легче сохранить Ваши шаблоны и их замены в a 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
Это будет довольно медленно для длинных списков шаблонов и больших файлов данных, таким образом, Вы могли бы хотеть считать шаблоны и создать a sed
сценарий от них вместо этого. Следующее принимает <пространство>, разделитель разделяет список СООТВЕТСТВИЯ <пространство> пары ЗАМЕНЫ, происходящие one-per-line в файле patterns.txt
:
sed 's| *\([^ ]*\) *\([^ ]*\).*|s/\1/\2/g|' <patterns.txt |
sed -f- ./editfile >outfile
Вышеупомянутый формат в основном произволен и, например, не допускает <пространство> или в СООТВЕТСТВИЯ или в ЗАМЕНЫ. Метод является очень общим хотя: в основном, если можно создать поток вывода, который похож на a sed
сценарий, затем можно получить тот поток как a sed
сценарий путем определения sed
файл сценария как -
stdin.
Можно объединить и связать несколько сценариев точно так же:
SOME_PIPELINE |
sed -e'#some expression script' \
-f./script_file -f- \
-e'#more inline expressions' \
./actual_edit_file >./outfile
POSIX sed
свяжет все сценарии в один в порядке, они появляются на командной строке. Ни одному из них не нужен конец в a \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
Замените любой из foo
, bar
или baz
с foobar
sed -Ei 's/foo|bar|baz/foobar/g' file
или
perl -i -pe 's/foo|bar|baz/foobar/g' file
Как выполнять поиск и замену в нескольких файлах предлагает:
Вы можно также использовать 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' )
С точки зрения пользователя, хороший и простой инструмент Unix, который отлично справляется со своей задачей, - это qsubst
. Например,
% qsubst foo bar *.c *.h
заменит foo
на bar
во всех моих файлах C. Приятной особенностью является то, что qsubst
выполнит query-replace , то есть покажет мне каждое вхождение foo
и спросит, хочу ли я заменить его или нет. [Вы можете безоговорочно заменить (без запроса) на параметр -go
, и есть другие варианты, например, -w
, если вы хотите заменить только foo
, когда это целое слово.]
Как это получить: qsubst
был изобретен дер Маусом (от МакГилла) и размещен в comp.unix.sources 11 (7) в Август 1987 г. Существуют обновленные версии. Например, версия NetBSD qsubst.c, v 1.8 2004/11/01
компилируется и отлично работает на моем Mac.
Я использовал это:
grep -r "old_string" -l | tr '\n' ' ' | xargs sed -i 's/old_string/new_string/g'
Список всех файлов, содержащих old_string
.
Замените новую строку в результате пробелами (чтобы список файлов можно было передать в sed
.
Запустите sed
для этих файлов, чтобы заменить старую строку новой.
Обновление: Приведенный выше результат не будет выполнен для имен файлов, содержащих пробелы. Вместо этого используйте:
grep --null -lr "old_string" | xargs --null sed -i 's / old_string / new_string / g '
Хорошим r e pl acement Linux инструментом является rpl , который изначально был написан для проекта Debian, поэтому он доступен с apt -get install rpl
в любом дистрибутиве, производном от Debian, и может быть для других, но в противном случае вы можете загрузить файл tar.gz
в SourgeForge .
Простейший пример использования:
$ rpl old_string new_string test.txt
Обратите внимание, что если строка содержит пробелы, ее следует заключить в кавычки. По умолчанию rpl
заботится о заглавных буквах , но не о полных словах , но вы можете изменить эти значения по умолчанию с помощью параметров -i
(игнорировать case) и -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
(подсказка) параметр:
Вывод показать количество замененных файлов / строк и тип поиска (с учетом регистра, целые / частичные слова), но он может не отображаться с помощью -q
( тихий режим ) вариант или даже более подробный, перечисляя номера строк, которые содержат совпадения каждого файла и каталога с параметром -v
( подробный режим ).
Также следует помнить о параметрах -e
(honor e scapes), которые разрешают регулярные выражения
, поэтому вы можете искать также вкладки ( \ t
), новые строки ( \ n
) и т. д. Даже вы можете использовать -f
до принудительно разрешить (конечно, только если у пользователя есть права на запись) и -d
для сохранения времени модификации`).
Наконец, если вы не уверены в том, что именно будет делать, используйте -s
( режим моделирования ).
Вы можете использовать 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
ripgrep(имя командыrg
)— это инструмент grep
, но он также поддерживает поиск и замену.
$ cat ip.txt
dark blue and light blue
light orange
blue sky
$ # by default, line number is displayed if output destination is stdout
$ # by default, only lines that matched the given pattern is displayed
$ # 'blue' is search pattern and -r 'red' is replacement string
$ rg 'blue' -r 'red' ip.txt
1:dark red and light red
3:red sky
$ # --passthru option is useful to print all lines, whether or not it matched
$ # -N will disable line number prefix
$ # this command is similar to: sed 's/blue/red/g' ip.txt
$ rg --passthru -N 'blue' -r 'red' ip.txt
dark red and light red
light orange
red sky
rg
не поддерживает вариант размещения -, так что вам придется сделать это самостоятельно
$ # -N isn't needed here as output destination is a file
$ rg --passthru 'blue' -r 'red' ip.txt > tmp.txt && mv tmp.txt ip.txt
$ cat ip.txt
dark red and light red
light orange
red sky
См. Документацию по регулярным выражениям Rust для ознакомления с синтаксисом и функциями регулярных выражений. Переключатель -P
включает вариант PCRE2 . rg
по умолчанию поддерживает Unicode.
$ # non-greedy quantifier is supported
$ echo 'food land bark sand band cue combat' | rg 'foo.*?ba' -r 'X'
Xrk sand band cue combat
$ # unicode support
$ echo 'fox:αλεπού,eagle:αετός' | rg '\p{L}+' -r '($0)'
(fox):(αλεπού),(eagle):(αετός)
$ # set operator example, remove all punctuation characters except. ! and ?
$ para='"Hi", there! How *are* you? All fine here.'
$ echo "$para" | rg '[[:punct:]--[.!?]]+' -r ''
Hi there! How are you? All fine here.
$ # use -P if you need even more advanced features
$ echo 'car bat cod map' | rg -P '(bat|map)(*SKIP)(*F)|\w+' -r '[$0]'
[car] bat [cod] map
Как и grep
, опция -F
позволит сопоставлять фиксированные строки, удобная опция, которую, как мне кажется, sed
тоже следует реализовать.
$ printf '2.3/[4]*6\nfoo\n5.3-[4]*9\n' | rg --passthru -F '[4]*' -r '2'
2.3/26
foo
5.3-29
Еще одна удобная опция — -U
, которая включает многострочное сопоставление
$ # (?s) flag will allow. to match newline characters as well
$ printf '42\nHi there\nHave a Nice Day' | rg --passthru -U '(?s)the.*ice' -r ''
42
Hi Day
rg
также может работать с файлами стиля dos -
$ # same as: sed -E 's/\w+(\r?)$/123\1/'
$ printf 'hi there\r\ngood day\r\n' | rg --passthru --crlf '\w+$' -r '123'
hi 123
good 123
Другое преимущество rg
заключается в том, что он, вероятно, будет быстрее, чемsed
$ # for small files, initial processing time of rg is a large component
$ time echo 'aba' | sed 's/a/b/g' > f1
real 0m0.002s
$ time echo 'aba' | rg --passthru 'a' -r 'b' > f2
real 0m0.007s
$ # for larger files, rg is likely to be faster
$ # 6.2M sample ASCII file
$ wget https://norvig.com/big.txt
$ time LC_ALL=C sed 's/\bcat\b/dog/g' big.txt > f1
real 0m0.060s
$ time rg --passthru '\bcat\b' -r 'dog' big.txt > f2
real 0m0.048s
$ diff -s f1 f2
Files f1 and f2 are identical
$ time LC_ALL=C sed -E 's/\b(\w+)(\s+\1)+\b/\1/g' big.txt > f1
real 0m0.725s
$ time rg --no-unicode --passthru -wP '(\w+)(\s+\1)+' -r '$1' big.txt > f2
real 0m0.093s
$ diff -s f1 f2
Files f1 and f2 are identical
zsh
. Любой ценой добавьтеzsh
информация, но нет никакой причины удалить материал удара. Кроме того, я знаю, что использование оболочки для обработки текста не идеально, но существуют случаи, где это необходимо. Я отредактировал в лучшей версии моего исходного сценария, который создаст ased
сценарий вместо того, чтобы на самом деле использовать цикл оболочки для парсинга. Это может быть полезно, если у Вас есть несколько сотен пар шаблонов, например. – terdon♦ 16.01.2015, 17:10(.)
спецификатор globbing так не может использоваться здесь. (Вы скучаете по некоторым - также). Для цикла является неправильным (отсутствующий-r) и означает делать несколько передач в файлах и не добавляет преимущества по sed сценарию. – Stéphane Chazelas 16.01.2015, 17:16--
послеsed -i
и перед командой замены указывают? – Geek 28.09.2015, 14:29-
. Используя его гарантирует, что команды будут работать над файлами с именами как-foo
. Без него,-f
был бы проанализирован как опция. – terdon♦ 28.09.2015, 14:42.git
каталог, и на самом деле портит Ваш контроль. Лучше работать в рамках/на определенных каталогов по имени. – Pistos 19.04.2016, 17:44