Не жадное (неохотное) соответствие регулярных выражений в sed? - PullRequest
376 голосов
/ 09 июля 2009

Я пытаюсь использовать sed для очистки строк URL-адресов, чтобы извлечь только домен ..

Так от:

http://www.suepearson.co.uk/product/174/71/3816/

Я хочу:

http://www.suepearson.co.uk/

(с косой чертой или без нее, это не имеет значения)

Я пытался:

 sed 's|\(http:\/\/.*?\/\).*|\1|'

и (избегая не жадного квантификатора)

sed 's|\(http:\/\/.*\?\/\).*|\1|'

но я не могу заставить работать не жадный квантификатор, поэтому он всегда будет соответствовать всей строке.

Ответы [ 21 ]

4 голосов
/ 01 августа 2016

Все еще есть надежда решить эту проблему с помощью чистого (GNU) sed. Несмотря на то, что это не универсальное решение, в некоторых случаях вы можете использовать «петли» для удаления всех ненужных частей строки, например:

sed -r -e ":loop" -e 's|(http://.+)/.*|\1|' -e "t loop"
  • -r: использовать расширенное регулярное выражение (для + и неэкранированных скобок)
  • ": loop": определить новую метку с именем "loop"
  • -e: добавить команды в sed
  • "t loop": вернуться к метке "loop", если произошла успешная замена

Единственная проблема здесь в том, что он также обрезает последний символ-разделитель ('/'), но если он вам действительно нужен, вы все равно можете просто положить его обратно после завершения цикла, просто добавьте эту дополнительную команду в конце предыдущей командной строки:

-e "s,$,/,"
3 голосов
/ 09 июля 2009
sed 's|(http:\/\/[^\/]+\/).*|\1|'
3 голосов
/ 09 июля 2009

sed -E интерпретирует регулярные выражения как расширенные (современные) регулярные выражения

Обновление: -E в MacOS X, -r в GNU sed.

2 голосов
/ 06 февраля 2014

Поскольку вы специально указали, что пытаетесь использовать sed (вместо perl, cut и т. Д.), Попробуйте группировать. Это позволяет обойтись без жадного идентификатора, который может быть не распознан. Первая группа - это протокол (то есть 'http://',' https://', 'tcp: //' и т. Д.). Вторая группа - это домен:

echo "http://www.suon.co.uk/product/1/7/3/" | sed "s|^\(.*//\)\([^/]*\).*$|\1\2|"

Если вы не знакомы с группировкой, запустите здесь .

1 голос
/ 26 июня 2018

Вот как надежно выполнять несжадное сопоставление многосимвольных строк, используя sed. Допустим, вы хотите изменить каждые foo...bar на <foo...bar>, например, такой ввод:

$ cat file
ABC foo DEF bar GHI foo KLM bar NOP foo QRS bar TUV

должно стать таким:

ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

Для этого вы конвертируете foo и bar в отдельные символы, а затем используете отрицание этих символов между ними:

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/g; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC <foo DEF bar> GHI <foo KLM bar> NOP <foo QRS bar> TUV

В вышеприведенном:

  1. s/@/@A/g; s/{/@B/g; s/}/@C/g преобразует { и } в строки-заполнители, которые не могут существовать во входных данных, поэтому эти символы доступны для преобразования foo и bar в.
  2. s/foo/{/g; s/bar/}/g преобразует foo и bar в { и } соответственно
  3. s/{[^{}]*}/<&>/g выполняет операцию, которую мы хотим - преобразование foo...bar в <foo...bar>
  4. s/}/bar/g; s/{/foo/g преобразует { и } обратно в foo и bar.
  5. s/@C/}/g; s/@B/{/g; s/@A/@/g преобразует строки-заполнители в их исходные символы.

Обратите внимание, что вышеприведенное не зависит от какой-либо конкретной строки, отсутствующей во входных данных, поскольку она производит такие строки на первом шаге, и не заботится о том, какое вхождение какого-либо конкретного регулярного выражения вы хотите сопоставить, так как вы можете использовать {[^{}]*} столько раз, сколько необходимо в выражении, чтобы выделить фактическое совпадение, которое вы хотите, и / или с оператором числового соответствия seds, например заменить только 2-е вхождение:

$ sed 's/@/@A/g; s/{/@B/g; s/}/@C/g; s/foo/{/g; s/bar/}/g; s/{[^{}]*}/<&>/2; s/}/bar/g; s/{/foo/g; s/@C/}/g; s/@B/{/g; s/@A/@/g' file
ABC foo DEF bar GHI <foo KLM bar> NOP foo QRS bar TUV
1 голос
/ 29 июня 2011

Я понимаю, что это старая запись, но кто-то может найти ее полезной. Поскольку полное доменное имя не должно превышать общую длину 253 символа, замените. * На. \ {1, 255 \}

0 голосов
/ 08 июня 2017

Вот что вы можете сделать с помощью двухэтапного подхода и awk:

A=http://www.suepearson.co.uk/product/174/71/3816/  
echo $A|awk '  
{  
  var=gensub(///,"||",3,$0) ;  
  sub(/\|\|.*/,"",var);  
  print var  
}'  

Выход: http://www.suepearson.co.uk

Надеюсь, это поможет!

0 голосов
/ 02 февраля 2016

Другая версия sed:

sed 's|/[:alphanum:].*||' file.txt

Соответствует /, за которым следует буквенно-цифровой символ (поэтому не еще один слеш), а также остальные символы до конца строки. После этого он заменяет его ничем (т.е. удаляет его).

0 голосов
/ 10 декабря 2010
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*|\1|'

не беспокойся, я получил это на другом форуме:)

0 голосов
/ 03 апреля 2019

Еще не видел этот ответ, поэтому вот как вы можете сделать это с vi или vim:

vi -c '%s/\(http:\/\/.\{-}\/\).*/\1/ge | wq' file &>/dev/null

Это запускает подстановку vi :%s глобально (завершающий g), воздерживается от выдачи ошибки, если шаблон не найден (e), затем сохраняет полученные изменения на диск и завершает работу. &>/dev/null предотвращает кратковременное мигание графического интерфейса на экране, что может раздражать.

Мне нравится использовать vi иногда для сверхсложных регулярных выражений, потому что (1) perl - dead dying, (2) vim имеет очень продвинутый механизм регулярных выражений, и (3 ) Я уже близко знаком с vi регулярными выражениями в моих ежедневных документах по редактированию использования.

...