
Я новичок в регулярных выражениях и sed и пытаюсь создать, как мне кажется, простое регулярное выражение: я хочу удалить последнюю букву слова, если это «о».
- Входная строка: Привет Привет
- Ожидаемый результат: Ад Ад
Хорошие новости: я могу удалить букву «о», если она находится в конце строки:
$ echo 'Hello Hello' |sed 's/\(.*\)o/\1/g'
Hello Hell
$ echo 'Hello Hello' |sed 's/\(.*\)o$/\1/g'
Hello Hell
Плохая новость: я не могу удалить его из слов, стоящих ранее в строке. Я пробовал это со всеми символами-якорями, которые только мог придумать. Результатом является то, что ни один из конечных «o» слова не удаляется:
$ echo 'Hello Hello' |sed 's/\(.*\)o\b/\1/g'
Hello Hello
$ echo 'Hello Hello' |sed 's/\(.*\)o\>/\1/g'
Hello Hello
$ echo 'Hello Hello' |sed 's/\(.*\)o\W/\1/g'
Hello Hello
$ echo 'Hello Hello' |sed 's/\(.*\)o\s/\1/g'
Hello Hello
Не могли бы вы помочь мне вернуть здравомыслие, объяснив, что я делаю не так?
Обновление: У меня сложилось четкое впечатление, что моя машина выдает результаты, отличные от результатов других людей. Я использую окно терминала на моем Macbook. Если кто-то может пролить свет на это, пожалуйста, сообщите мне.
решение1
echo 'Hello Hello' | sed 's/o$//'
кажется мне более полезным, чем ваш
echo 'Hello Hello' | sed 's/\(.*\)o$/\1/g'
В вашем вопросе говорится, что выход
echo 'Hello Hello' | sed 's/\(.*\)o\b/\1/g'
было Hello Hello
, но для меня это так Hello Hell
. Вы можете исправить это на
echo 'Hello Hello' | sed 's/\([^o]*\)o\b/\1/g'
но
echo 'Hello Hello' | sed 's/o\b//g'
Мне кажется, так лучше.
решение2
Удаление o
в конце слов — это удаление ao между символом слова и несловесным символом (или EOL), поэтому:
sed -r 's/(\w)o(\W|$)/\1\2/g'
решение3
Мне интересно, space
не является ли ваш разделитель слов чем-то другим. Попробуйте что-то вроде следующего:
$ echo hello hello | sed -e 's/o / /g;s/o$//'
hell hell
Проблема с этим примером в том, что вам также придется сделать то же самое для .
and ,
и любого другого разделителя слов. Сопоставьте, o
за которым следует другой определенный символ с помощью []
like o[ \.,]
. По какой-то причине это не работает для EOL $
, поэтому добавьте еще одну строку поиска с помощью ;
. Пример:
$ echo hello hello, hello. toot hello | sed -e 's/o\([ \.,]\)/\1/g;s/o$//'
hell hell, hell. toot hell
$ echo $SHELL
/bin/bash
$ sed --version
sed (GNU sed) 4.4
$ set | grep IFS
IFS=$' \t\n'
решение4
Я попробовал это со всеми символами-якорями, которые только смог придумать.
Это не якоря, а то, что у вас жадный матч со звездочкой. Выражение \(.*\)o
совпадаеткак можно длиннее, поэтому он съест все до самогопоследний o
. Это может совпадать o
и с более ранними .
Но тогда бесполезно что-то захватывать и возвращать обратно, можно просто удалить и то, \(.*\)
и другое \1
полностью.
Итак, это (по крайней мере в GNU sed) удалит o
«'» в конце слов:
sed 's/o\>//g'
sed 's/o\b//g'
Это, конечно, только в конце строки:
sed 's/o$//g'
И это удалит o
, а также следующий за ним символ, не являющийся словом (например, пробел после Hello
):
sed 's/o\W//g'
Если ваш sed
не поддерживает \<
/ \>
или \b
, вам придется сделать что-то еще. Это будет соответствовать o
следующему небуквенно-цифровому символу или концу строки:
$ echo "jello, jello" | sed -E -e 's/o([^[:alnum:]]|$)/\1/g'
jell, jell
Это работает, например, в sed
ОС OS X/macOS.
Регулярные выражения Perl поддерживают добавление вопросительного знака к *
или , +
чтобы сделать их нежадными. Тогда они будут соответствоватьсамый короткийвозможная строка:
echo "jello, jello" | perl -pe 's/(.*?)o/$1/g'
jell, jell