Не жадное (неохотное) сопоставление регулярных выражений в sed?

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

Итак, из:

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

Я хочу:

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

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

Я пытался:

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

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

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

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


person Joel    schedule 09.07.2009    source источник
comment
Примечание: если вы разделяете регулярные выражения с помощью |, вам не нужно экранировать / s. Фактически, большинство людей разграничивают | вместо / s, чтобы избежать заборов.   -  person AttishOculus    schedule 14.11.2009
comment
@AttishOculus Первый символ после 's' в выражении замены в sed - это разделитель. Отсюда s ^ foo ^ bar ^ 'или' s! Foo! Bar! ' также работаю   -  person Squidly    schedule 06.02.2014
comment
Для расширенного регулярного выражения используйте sed -E 's.... Тем не менее, ни один упорный оператор.   -  person Ondra Žižka    schedule 03.05.2018
comment
Не отвечаю на заголовок вопроса, но в данном конкретном случае работает простой cut -d'/' -f1-3.   -  person Petr Javorik    schedule 17.10.2019


Ответы (24)


Ни базовое, ни расширенное регулярное выражение Posix / GNU не распознает нежадный квантор; вам нужно более позднее регулярное выражение. К счастью, регулярное выражение Perl для этого контекста довольно легко получить:

perl -pe 's|(http://.*?/).*|\1|'
person chaos    schedule 09.07.2009
comment
Для этого используйте опции -pi -e. - person reallynice; 10.12.2013
comment
Боже правый, я не могу поверить, что это сработало :-) Единственное, что отстой, теперь мой скрипт имеет зависимость Perl :-( С другой стороны, практически в каждом дистрибутиве Linux уже есть Perl, поэтому, вероятно, не проблема :-) - person Freedom_Ben; 20.09.2014
comment
@Freedom_Ben: IIRC perl требуется для POSIX - person MestreLion; 30.08.2015
comment
К сожалению, это не указывает на совпадения. Есть способ исправить? - person Hi-Angel; 21.10.2015
comment
@MestreLion {{необходима цитата}} Я не думаю, что это произойдет в ближайшее время - person xhienne; 14.12.2016
comment
Я думаю, что это в LSB, хотя - person Daniel H; 06.02.2017
comment
Не дает решения для вопроса sed. - person dolphus333; 11.10.2017
comment
@ dolphus333: ни базовое, ни расширенное регулярное выражение Posix / GNU не распознает не жадный квантификатор, что означает, что вы не можете использовать нежадный квантификатор в sed. - person chaos; 18.10.2017
comment
какое отношение этот ответ имеет к sed? - Sérgio 31 дек. - person Sérgio; 03.01.2018
comment
@ Sérgio - это то, как вы выполняете запрошенную вещь, что невозможно в sed, используя синтаксис, в основном идентичный синтаксису sed - person chaos; 06.01.2018
comment
Старый добрый Perl. Системные администраторы игнорируют Perl в ущерб себе. Даже Python не может делать однострочники, как Perl. Я обнаружил, что он уже установлен на моем компьютере с Fedora 28, но я думаю, что кто-то от него зависит. Хорошая работа, кто-нибудь! - person Mike S; 12.07.2018

В этом конкретном случае вы можете выполнить работу без использования нежадного регулярного выражения.

Попробуйте это нежадное регулярное выражение [^/]* вместо .*?:

sed 's|\(http://[^/]*/\).*|\1|g'
person Gumbo    schedule 09.07.2009
comment
Как сделать так, чтобы sed не жадно соответствовал фразе, используя эту технику? - person user3694243; 08.10.2016
comment
К сожалению, вы не можете; см. ответ хаоса. - person Daniel H; 06.02.2017
comment
Большое спасибо ... поскольку perl больше не входит в стандартную базу установки во многих дистрибутивах Linux! - person st0ne; 27.09.2017
comment
@DanielH Фактически, можно не жадно сопоставлять фразы с помощью этой техники, как требуется. Просто может потребоваться некоторая боль, чтобы написать любой шаблон с достаточной точностью. Например. при синтаксическом анализе назначения значения ключа в запросе URL может потребоваться поиск назначения с использованием ([^&=#]+)=([^&#]*). Есть случаи, которые точно не работают, например при синтаксическом разборе URL-адреса для его части хоста и имени пути с последней косой чертой предполагается, что он необязателен для исключения из захвата: ^(http:\/\/.+?)/?$ - person Thomas Urban; 17.03.2020
comment
Этот ответ должен быть предпочтительным для предотвращения нежелательных шаблонов вообще. Не используйте нежадные квантификаторы, если использование более конкретных шаблонов не помогает существующим штраф за производительность. - person Thomas Urban; 17.03.2020
comment
Это мой предпочтительный ответ, но давайте проясним, что sed все еще использует жадное сопоставление, мы просто создаем шаблон, в котором жадное сопоставление завершается там, где мы хотим. - person Stephen P; 15.10.2020

В sed я обычно реализую нежадный поиск, ища все, кроме разделителя, до разделителя:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*\)/.*;\1;p'

Выход:

http://www.suon.co.uk

это:

  • не выводить -n
  • поиск, соответствие шаблону, замена и печать s/<pattern>/<replace>/p
  • используйте ; разделитель команд поиска вместо /, чтобы упростить ввод, поэтому s;<pattern>;<replace>;p
  • запомнить соответствие между скобками \( ... \), позже будет доступно с _10 _, _ 11 _...
  • совпадение http://
  • за которым следует что-либо в скобках [], [ab/] будет означать либо a, либо b, либо /
  • первый ^ в [] означает not, поэтому за ним следует что-нибудь, кроме того, что в []
  • поэтому [^/] означает что угодно, кроме символа /
  • * означает повторение предыдущей группы, поэтому [^/]* означает символы, кроме /.
  • пока sed -n 's;\(http://[^/]*\) означает поиск и запоминание http://, за которым следуют любые символы, кроме /, и запоминание того, что вы нашли
  • мы хотим искать до конца домена, поэтому остановитесь на следующем /, поэтому добавьте еще / в конце: sed -n 's;\(http://[^/]*\)/', но мы хотим сопоставить остальную часть строки после домена, поэтому добавьте .*
  • теперь совпадение, запомненное в группе 1 (\1), является доменом, поэтому замените совпавшую строку на материал, сохраненный в группе \1, и напечатайте: sed -n 's;\(http://[^/]*\)/.*;\1;p'

Если вы хотите включить обратную косую черту после домена, добавьте еще одну обратную косую черту в группу, чтобы запомнить:

echo "http://www.suon.co.uk/product/1/7/3/" | sed -n 's;\(http://[^/]*/\).*;\1;p'

выход:

http://www.suon.co.uk/
person stefanB    schedule 20.12.2012
comment
Относительно недавних правок: круглые скобки - это своего рода символ скобок, поэтому называть их скобками не неправильно, особенно если вы ставите после слова фактические символы, как это сделал автор. Кроме того, это предпочтительное использование в некоторых культурах, поэтому замена его предпочтительным использованием в вашей собственной культуре кажется немного грубой, хотя я уверен, что это не то, что задумал редактор. Лично я считаю, что лучше использовать чисто описательные имена, такие как круглые скобки, квадратные скобки и угловые скобки. - person Alan Moore; 10.04.2014
comment
Можно ли заменить разделитель на строку? - person Calculemus; 25.06.2014

Имитация ленивого (не жадного) квантификатора в sed

И все остальные варианты регулярных выражений!

  1. Поиск первого вхождения выражения:

    • POSIX ERE (с использованием параметра -r)

      Регулярное выражение:

        (EXPRESSION).*|.
      

      Сед:

        sed -r ‍'s/(EXPRESSION).*|./\1/g' # Global `g` modifier should be on
      

      Пример (поиск первой последовательности цифр) Live demo:

        $ sed -r 's/([0-9]+).*|./\1/g' <<< 'foo 12 bar 34'
      
        12
      

      Как это работает?

      Это регулярное выражение выигрывает от чередования |. В каждой позиции движок пытается выбрать самое длинное совпадение (это стандарт POSIX, за которым также следуют несколько других движков), что означает, что он использует ., пока не будет найдено совпадение для ([0-9]+).*. Но порядок тоже важен.

      введите описание изображения здесь

      Поскольку установлен глобальный флаг, движок пытается продолжить сопоставление символа за символом до конца входной строки или нашей цели. Как только первая и единственная группа захвата левой стороны чередования совпадает, (EXPRESSION) остальная часть строки также потребляется .*. Теперь мы сохраняем нашу ценность в первой группе захвата.

    • # P11 # # P12 #
        \(\(\(EXPRESSION\).*\)*.\)*
      
      # P13 #
        sed 's/\(\(\(EXPRESSION\).*\)*.\)*/\3/'
      
      # P14 #
        $ sed 's/\(\(\([0-9]\{1,\}\).*\)*.\)*/\3/' <<< 'foo 12 bar 34'
      
        12
      
      # P15 #
      # P16 #
      # P17 #
  2. # P18 # # P19 #
    sed 's/\(END-DELIMITER-EXPRESSION\).*/\1/; \
         s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*/\1/g'
    
    # P20 #
    foobar start block #1 end barfoo start block #2 end
    
    # P21 # # P22 #
    $ sed 's/\(end\).*/\1/; s/\(\(start.*\)*.\)*/\1/g'
    
    # P23 #
    start block #1 end
    
    # P24 #
    # P25 #
    # P26 #
    # P27 #

Непосредственно отвечая на ваш вопрос

Используя подход №2 (выражение с разделителями), вы должны выбрать два подходящих выражения:

  • EDE: [^:/]\/

  • SDE: http:

Использование:

$ sed 's/\([^:/]\/\).*/\1/g; s/\(\(http:.*\)*.\)*/\1/' <<< 'http://www.suepearson.co.uk/product/174/71/3816/'

Выход:

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

Примечание: это не сработает с идентичными разделителями.

person revo    schedule 28.09.2016
comment
3) предлагая такие сайты, как regex101 для демонстрации, добавьте примечание, что он не всегда подходит для инструментов cli из-за различий в синтаксисе и функциях. - person Sundeep; 27.04.2020
comment
@Sundeep Спасибо. Я превратил все эти кавычки в одинарные. Также я считал, что следует упомянуть самое левое правило самого длинного совпадения. Однако в sed и всех других движках следование тому же стандартному порядку имеет значение, когда дело доходит до равенства. Итак, echo 'foo 1' | sed -r 's/.|([0-9]+).*/\1/g' нет совпадения, но echo 'foo 1' | sed -r 's/([0-9]+).*|./\1/g' есть. - person revo; 27.04.2020
comment
@Sundeep также обходной путь для выражений с разделителями не работал для идентичных разделителей начала и конца, для которых я добавил примечание. - person revo; 27.04.2020
comment
Замечательный момент о том, что происходит, когда разные чередования начинаются с одного и того же места и имеют одинаковую длину, предположим, что они будут следовать в порядке слева направо, как и другие двигатели .. нужно искать, если это описано в руководстве - person Sundeep; 27.04.2020
comment
здесь есть странный случай: stackoverflow.com/questions/59683820/ - person Sundeep; 27.04.2020

sed не поддерживает "не жадный" оператор.

Вы должны использовать оператор «[]», чтобы исключить «/» из совпадения.

sed 's,\(http://[^/]*\)/.*,\1,'

P.S. Нет необходимости использовать обратную косую черту "/".

person andcoz    schedule 09.07.2009
comment
не совсем. если разделитель может быть одним из многих возможных символов (скажем, только строкой чисел), ваше отрицательное совпадение может становиться все более и более сложным. это нормально, но было бы неплохо иметь возможность сделать. * не жадным - person gesell; 28.07.2016
comment
Вопрос был более общим. Эти решения работают для URL-адресов, но не (например) для моего варианта использования удаления конечных нулей. s/([[:digit:]]\.[[1-9]]*)0*/\1/ явно не годится для 1.20300. Однако, поскольку исходный вопрос касался URL-адресов, их следует упомянуть в принятом ответе. - person Daniel H; 06.02.2017

Нежадное решение для более чем одного символа

Эта ветка действительно старая, но я предполагаю, что она все еще нужна людям. Допустим, вы хотите убить все до самого первого появления HELLO. Вы не можете сказать _2 _...

Итак, хорошее решение включает в себя два шага, предполагая, что вы можете сэкономить уникальное слово, которое вы не ожидаете во входных данных, скажем top_sekrit.

В этом случае мы можем:

s/HELLO/top_sekrit/     #will only replace the very first occurrence
s/.*top_sekrit//        #kill everything till end of the first HELLO

Конечно, при более простом вводе вы можете использовать меньшее слово или, может быть, даже один символ.

HTH!

person ishahak    schedule 30.10.2013
comment
Чтобы сделать его еще лучше, полезно в ситуации, когда вы не можете ожидать неиспользуемого символа: 1. замените этот специальный символ на действительно неиспользуемое СЛОВО, 2. замените конечную последовательность специальным символом, 3. выполните поиск, заканчивающийся специальным символом, 4 .. заменить спецсимвол обратно, 5. заменить спецслово обратно. Например, вам нужен жадный оператор между ‹hello› и ‹/hello›: - person Jakub; 27.05.2014
comment
Вот пример: echo Найти: ‹hello› fir ~ st ‹br› yes ‹/hello› ‹hello› sec ~ ond ‹/hello› | sed -es, ~, ОЧЕНЬ СПЕЦИАЛЬНЫЙ, g -es, ‹/hello›, ~, g -es,. * Найдите: ‹hello› ([^ ~] *). *, \ 1, -es, \ ~, ‹ / привет ›, -es, ОЧЕНЬ СПЕЦИАЛЬНО, ~, - person Jakub; 27.05.2014
comment
Я согласен. хорошее решение. Я бы перефразировал комментарий следующим образом: если вы не можете полагаться на то, что ~ не используется, сначала замените его текущие вхождения с помощью s / ~ / VERYspeciaL / g, затем выполните описанный выше трюк, а затем верните исходный ~ с помощью s / VERYspeciaL / ~ / g - person ishahak; 28.05.2014
comment
Мне нравится использовать более редкие переменные для такого рода вещей, поэтому вместо ` я бы использовал <$$> (поскольку $$ расширяется до идентификатора вашего процесса в оболочке, хотя вам придется использовать двойные кавычки, а не одинарные кавычки, и это может нарушить другие части вашего регулярного выражения) или, если доступен юникод, что-то вроде <∈∋>. - person Adam Katz; 09.02.2016
comment
В какой-то момент вы должны спросить себя, почему вы не используете вместо этого просто perl или python или какой-то другой язык. perl делает это менее хрупким образом в одну строчку ... - person ArtOfWarfare; 05.06.2017
comment
Это дело личного вкуса. Мне нравится делать что-то в сценариях sed. Это своего рода вызов. Перл или Python - мои запасные планы. И если мой сценарий sed хорошо документирован, я не вижу преимущества других языков перед ним. Но, возможно, вам стоит направить свой комментарий на Quora - person ishahak; 06.06.2017

sed - ненадежное сопоставление от Кристофа Сигхарта

Уловка для получения не жадного сопоставления в sed состоит в том, чтобы сопоставить все символы, за исключением того, который завершает сопоставление. Я знаю, это понятно, но я потратил на это драгоценные минуты, а сценарии оболочки, в конце концов, должны быть быстрыми и легкими. Итак, если это может понадобиться кому-то другому:

Жадное соответствие

% echo "<b>foo</b>bar" | sed 's/<.*>//g'
bar

Нежадное сопоставление

% echo "<b>foo</b>bar" | sed 's/<[^>]*>//g'
foobar
person gresolio    schedule 12.10.2017

Это можно сделать с помощью cut:

echo "http://www.suepearson.co.uk/product/174/71/3816/" | cut -d'/' -f1-3
person Dee    schedule 10.12.2010

другой способ, не используя регулярное выражение, - использовать метод полей / разделителя, например

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print $1,$2,$3}' OFS="/"
person ghostdog74    schedule 09.07.2009

sed безусловно, имеет свое место, но это не одно из них!

Как заметил Ди: просто используйте cut. В этом случае это намного проще и безопаснее. Вот пример, в котором мы извлекаем различные компоненты из URL-адреса с помощью синтаксиса Bash:

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

protocol=$(echo "$url" | cut -d':' -f1)
host=$(echo "$url" | cut -d'/' -f3)
urlhost=$(echo "$url" | cut -d'/' -f1-3)
urlpath=$(echo "$url" | cut -d'/' -f4-)

дает тебе:

protocol = "http"
host = "www.suepearson.co.uk"
urlhost = "http://www.suepearson.co.uk"
urlpath = "product/174/71/3816/"

Как видите, это гораздо более гибкий подход.

(все кредиты Ди)

person peterh    schedule 30.08.2013

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

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

person stepancheg    schedule 09.07.2009
comment
Нет, это не так ... По крайней мере, не GNU sed. - person Michel de Ruiter; 01.02.2011
comment
В более широком смысле -E уникален для BSD sed и, следовательно, для OS X. Ссылки на страницы руководства. -r добавляет расширенные регулярные выражения в GNU sed как отмечен в исправлении @ stephancheg. Остерегайтесь использования команды с известной изменчивостью в разных дистрибутивах. Я узнал это на собственном горьком опыте. - person fny; 24.05.2012
comment
Это правильный ответ, если вы хотите использовать sed, и он наиболее подходит для исходного вопроса. - person Will Tice; 22.07.2013
comment
Параметр -r GNU sed изменяет только правила экранирования в соответствии с Appendix A Extended regular expressions информационного файла и некоторыми быстрыми тестами; на самом деле он не добавляет нежадный квалификатор (по крайней мере, с GNU sed version 4.2.1.) - person eichin; 01.09.2013
comment
@eichin, в ERE вместо BRE добавлено больше, чем просто правила экранирования, но на самом деле нежадное сопоставление не входит в их число. - person Charles Duffy; 05.02.2015
comment
GNU sed на какое-то время распознал -E как недокументированный вариант, но в версии 4.2.2.177, документация была обновлена, чтобы отразить это, так что -E подходит для обоих сейчас. - person Benjamin W.; 10.05.2017

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

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

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

-e "s,$,/,"
person mTUX    schedule 01.08.2016

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

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

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

person BrianB    schedule 06.02.2014

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

person Iain Henderson    schedule 29.06.2011

Вот как надежно выполнять нежадное сопоставление многосимвольных строк с помощью 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
person Ed Morton    schedule 26.06.2018

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

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

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

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

person Luke Davis    schedule 03.04.2019

echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*|\1|'

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

person Dee    schedule 10.12.2010
comment
так что вы получите жадное совпадение: /home/one/two/three/, если вы добавите еще /, например /home/one/two/three/four/myfile.txt, вы также с жадностью найдете four: /home/one/two/three/four, вопрос касается нежадного - person stefanB; 21.12.2012

sed 's|\(http:\/\/www\.[a-z.0-9]*\/\).*|\1| тоже работает

person GL2014    schedule 24.06.2013

Вот что можно сделать с помощью двухэтапного подхода и 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

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

person VINAY NAIR    schedule 08.06.2017

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

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

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

person sycamorex    schedule 02.02.2016
comment
Думаю, это должно быть "[[:alnum:]]", а не "[:alphanum:]". - person oli_arborum; 30.09.2019

@Daniel H (относительно вашего комментария к ответу andcoz, хотя и давно): удаление конечных нулей работает с

s,([[:digit:]]\.[[:digit:]]*[1-9])[0]*$,\1,g

речь идет о четком определении условий сопоставления ...

person Volker    schedule 27.07.2020

Вы также должны подумать о случае, когда нет совпадающих разделителей. Вы хотите вывести строку или нет. Мои примеры здесь ничего не выводят, если совпадений нет.

Вам нужен префикс до 3-го /, поэтому выберите дважды строку любой длины, не содержащую / и следующую за /, а затем строку любой длины, не содержащую /, а затем сопоставьте / после любой строки, а затем распечатайте выделение. Эта идея работает с любыми символами-ограничителями.

echo http://www.suepearson.co.uk/product/174/71/3816/ | \
  sed -nr 's,(([^/]*/){2}[^/]*)/.*,\1,p'

Используя команды sed, вы можете быстро удалить префикс или выбрать разделитель, например:

echo 'aaa @cee: { "foo":" @cee: " }' | \
  sed -r 't x;s/ @cee: /\n/;D;:x'

Это намного быстрее, чем есть уголь за раз.

Перейти к метке, если предыдущее совпадение было успешным. Добавьте \ n в / перед 1-м разделителем. Удалить до первого \ n. Если был добавлен \ n, перейдите в конец и напечатайте.

Если есть начальные и конечные разделители, просто удалить конечные разделители, пока вы не дойдете до элемента nth-2, который вы хотите, а затем выполните трюк D, удалите после конечного разделителя, перейдите, чтобы удалить, если нет совпадения, удалите до начала разделителя и и Распечатать. Это работает, только если начальные / конечные разделители встречаются парами.

echo 'foobar start block #1 end barfoo start block #2 end bazfoo start block #3 end goo start block #4 end faa' | \
  sed -r 't x;s/end//;s/end/\n/;D;:x;s/(end).*/\1/;T y;s/.*(start)/\1/;p;:y;d'
person Markus Linnala    schedule 11.06.2021

Если у вас есть доступ к gnu grep, вы можете использовать регулярное выражение perl:

grep -Po '^https?://([^/]+)(?=)' <<< 'http://www.suepearson.co.uk/product/174/71/3816/'
http://www.suepearson.co.uk

В качестве альтернативы, чтобы получить все после домена, используйте

grep -Po '^https?://([^/]+)\K.*' <<< 'http://www.suepearson.co.uk/product/174/71/3816/'
/product/174/71/3816/
person laur    schedule 19.06.2021

person    schedule
comment
Если вы используете | в качестве разделителя нет необходимости экранировать /. - person Michael Back; 30.10.2015