Регистро-независимого поиска и замены с помощью sed
Я пытаюсь использовать sed для извлечения текста из файла журнала.
Я могу сделать поиск и замену без особых проблем:
sed 's/foo/bar/' mylog.txt
тем не менее, я хочу сделать поиск без учета регистра. Из того, что я погуглил, это похоже на добавление i
до конца команда должна работать:
sed 's/foo/bar/i' mylog.txt
, это дает мне сообщение об ошибке:
sed: 1: "s/foo/bar/i": bad flag in substitute command: 'i'
что здесь не так, и как это исправить?
Я на macOS, в случае вопросы.
8 ответов:
чтобы было понятно: On macOS - по состоянию на Сьерра (10.12) -
sed
- это BSD реализация-не поддерживает сопоставление без учета регистра - трудно поверить, но это правда. Элемент ранее принятый ответ, что само по себе показывает GNUsed
команда, получила этот статус из-заperl
- решение на основе упомянутых в комментариях.чтобы сделать это Perl solution работа с символы также, через UTF-8, используйте что-то вроде:
perl -C -Mutf8 -pe 's/öœ/oo/i' <<< "FÖŒ" # -> "Foo"
-C
включает поддержку UTF-8 для потоков и файлов, предполагая, что текущая локаль основана на UTF-8.-Mutf8
говорит Perl интерпретировать исходный код как UTF-8 (в этом случае строка передается в-pe
) - это более короткий эквивалент более многословным-e 'use utf8;'.
спасибо, Марк Рид(обратите внимание, что используя
awk
тоже не вариант, аawk
на macOS (т. е. BWK awk, а.к.а. BSD awk) кажется, совершенно не знают о местах вообще-егоtolower()
иtoupper()
функции игнорируют внешние символы (иsub()
/gsub()
не имеют флагов нечувствительности к регистру для начала).)
еще один обход для
sed
на Mac OS X это установитьgsed
из MacPorts или HomeBrew, а затем создать псевдонимsed='gsed'
.
версия для Mac
sed
кажется немного ограниченным. Один из способов обойти это-использовать контейнер linux (через Docker), который имеет полезную версиюsed
:cat your_file.txt | docker run -i busybox /bin/sed -r 's/[0-9]{4}/****/Ig'
The sed FAQ адресов, тесно связанных с учетом регистра поиск. Он указывает, что a) многие версии sed поддерживают флаг для него и b) это неудобно делать в sed, вы должны скорее использовать awk или Perl.
но чтобы сделать это в POSIX sed, они предлагают три варианта (адаптированные для замены здесь):
преобразование в верхний регистр и сохранение исходной строки в пространстве удержания; это не будет работать для замен, хотя, как исходное содержимое будет восстановлено перед печатью, поэтому оно подходит только для вставки или добавления строк на основе совпадения без учета регистра.
возможно, возможности ограничены
FOO
,Foo
иfoo
. Они могут быть покрытыs/FOO/bar/;s/[Ff]oo/bar/
для поиска всех возможных совпадений можно использовать скобочные выражения для каждого символа:
s/[Ff][Oo][Oo]/bar/
у меня была аналогичная потребность, и я придумал это:
эта команда просто найти все файлы:
grep -i -l -r foo ./*
это один, чтобы исключить this_shell.sh (в случае, если вы поместите команду в скрипт с именемthis_shell.sh), tee вывод на консоль, чтобы увидеть, что произошло, а затем использовать sed на каждое имя файла, найденное для замены текста foo на bar:
grep -i -l -r --exclude "this_shell.sh" foo ./* | tee /dev/fd/2 | while read -r x; do sed -b -i 's/foo/bar/gi' "$x"; done
Я выбрал этот метод, так как мне не понравилось, что все временные метки изменились для файлов нет модифицированный. подача результата grep позволяет просматривать только файлы с целевым текстом (таким образом, вероятно, может улучшить производительность / скорость)
Не забудьте сделать резервную копию файлов и проверить перед использованием. Может не работать в некоторых средах для файлов со встроенными помещениями. (?)