
Представьте, что у меня есть что-то вроде следующего текста:
Быстрые прыжки бурой лисицы в 2012 и 2013 годах
И я хотел бы удалить часть из слова «лиса», включая четыре цифры, но только в первом случае, поэтому в итоге у меня получится:
Быстро коричневый и 2013
Что-то вроде этого...:
echo "Быстрые прыжки бурой лисы в 2012 и 2013 годах" \ | sed "с/лиса.*\([0-9]\{4\}\)//г"
...приносит мне:
Быстро коричневый
Поэтому он удалил все, включая последнее вхождение четырех цифр.
Есть идеи?
решение1
Регулярные выражения POSIX, используемые sed
(как «базовая», так и «расширенная» версии), не поддерживают нежадные совпадения. (Хотя есть некоторые обходные пути, такие как использование [^0-9]*
вместо .*
, они становятся ненадежными, если входные данные сильно различаются.)
То, что вам нужно, можно получить в Perl, используя ?
нежадный квантификатор:
echo "The quick brown fox jumps in 2012 and 2013" \
| perl -pe 's/fox.*?([0-9]{4})//g'
Возможно, вы также захотите удалить лишний пробел.
решение2
Предполагая, что вы хотите использоватьтолькоsed и вы хотите, чтобы концом совпадения была первая группа цифр, не заботясь о том, какое слово следует после цифр, это работает:
echo "Быстрые прыжки бурой лисы в 2012 и 2013 годах" \ | sed "с/лиса[^0-9][^0-9]*[0-9][0-9]* //"
Шаблон работает путем сопоставления fox
, за которым следует один или несколько нецифровых символов [^0-9][^0-9]*
, за которыми следует 1 или несколько цифр [0-9][0-9]*
. Этот шаблон будет работать с произвольным количеством цифр, а не только с 4. Если вы хотите сопоставить ровно 4 цифры, измените его на:
echo "Быстрые прыжки бурой лисы в 2012 и 2013 годах" \ | sed "s/fox[^0-9]*\([0-9]\{4\}\) //"
решение3
Вы не указалиточнокаковы ваши требования. Возможно, вам нужен многошаговый процесс. Выберите строку, которая, как вы знаете, не будет встречаться в вашем вводе (например, ####
):
echo "Быстрая коричневая лиса перепрыгнула через 42 ленивых собаки в 2012 и 2013 годах." \ | сэд \ -e "с/[0-9]\{4\}/&####/" \ -e "с/лиса.*####//" \ -е "с/####//"
(Команда чрезмерно свернута для удобства чтения.) -e "s/[0-9]\{4\}/&####/"
Вводит ####
послепервыйчетырехзначное число. (Внимание: это изменится 65536
на 6553####6
.)
-e "s/fox.*####//"
влияет на строки, содержащие fox
и ####
-- т.е. строки, содержащие по крайней мере одно четырехзначное число -- и затем удаляет fox
изпервыйчетырехзначное число.
-e "s/####//"
, конечно, очищает все ####
строки, оставшиеся от строк, содержащих четырехзначное число, но не fox
.
Также удалить один пробел после числа, если он есть,
echo "Быстрая коричневая лиса перепрыгнула через 42 ленивых собаки в 2012 и 2013 годах." \ | сэд \ -e "с/[0-9]\{4\}/&####/" \ -e "с/лиса.*#### //" \ -e "с/лиса.*####//" \ -е "с/####//"
Предупреждение: Вы можете добавить g
ко всем s
командам, но, поскольку это все еще использует .*
, что является корнем вашей проблемы, это все равно не будет обработано
One fox jumps in 2012 and 2013, another fox will jump in 2014 and 2015.
так, как вы, вероятно, хотите. И, конечно, вынехотите добавить g
, "s/[0-9]\{4\}/&####/"
потому что тогда он будет внедряться ####
послекаждыйчетырехзначное число, разрушающее весь смысл. Тогда в "s/fox.*####//"
конечном итоге будет действовать так же, как "s/fox.*[0-9]\{4\}//"
(ваша исходная команда с удаленными не участвующими символами); т. е. она изменится
Быстрые прыжки бурой лисицы в 2012 и 2013 годах.
к
Быстрые прыжки бурой лисы в 2012#### и 2013####.
а затем к
Быстрое коричневое.