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



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



Так с:



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


хочу:



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



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



Я пробовал:



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


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



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


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

615   20  

20 ответов:

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

perl -pe 's|(http://.*?/).*||'

попробовать [^/]* вместо .*?:

sed 's|\(http://[^/]*/\).*||g'

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

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

выход:

http://www.suon.co.uk

это:

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

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

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

выход:

http://www.suon.co.uk/

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

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

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

С. П. Нет необходимости в обратной косой черты "/".

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

и все другие вкусы регулярных выражений!

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

    • POSIX ERE (через )

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

      (EXPRESSION).*|.
      

      Sed:

      sed -r "s/(EXPRESSION).*|.//g" # Global `g` modifier should be on
      

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

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

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

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

      enter image description here

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

    • POSIX BRE

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

      \(\(\(EXPRESSION\).*\)*.\)*
      

      Sed:

      sed "s/\(\(\(EXPRESSION\).*\)*.\)*//"
      

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

      $ sed "s/\(\(\([0-9]\{1,\}\).*\)*.\)*//" <<< "foo 12 bar 34"
      
      12
      

      это похоже на версию ERE, но без изменения. Вот и все. В каждой отдельной позиции двигатель пытается соответствовать цифре.

      enter image description here

      если он найден, другие следующие цифры потребляются и захватываются, а остальная часть строки немедленно сопоставляется в противном случае, так как * означает больше или равно нулю он пропускает вторую группу захвата \(\([0-9]\{1,\}\).*\)* и прибывает в точку . чтобы соответствовать одному символу, и этот процесс продолжается.

  2. найти первое вхождение a с разделителями выражение:

    этот подход будет соответствовать самому первому вхождению строки, которая разделена. Мы можем назвать это блоком строк.

    sed "s/\(END-DELIMITER-EXPRESSION\).*//; \
         s/\(\(START-DELIMITER-EXPRESSION.*\)*.\)*//g"
    

    входной строки:

    foobar start block #1 end barfoo start block #2 end
    

    -Эде: end

    - SDE: start

    $ sed "s/\(end\).*//; s/\(\(start.*\)*.\)*//g"
    

    выход:

    start block #1 end
    

    первое выражение \(end\).* соответствует и захватывает первый конечный разделитель end и заменяет все совпадения с последними захваченными символами, которые является конечным разделителем. На данном этапе наш выход: foobar start block #1 end.

    enter image description here

    затем результат передается во второе регулярное выражение \(\(start.*\)*.\)* это то же самое, что и версия POSIX BRE выше. Он соответствует одному символу если начать разделитель start не соответствует в противном случае он соответствует и захватывает начальный разделитель и соответствует остальным символам.

    enter image description here


прямой ответ на ваш вопрос

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

  • Эде: [^:/]\/

  • SDE: http:

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

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

выход:

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

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

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

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

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

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

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

НТН!

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

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

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

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

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

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

не жадный, соответствующего

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

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

string="http://www.suepearson.co.uk/product/174/71/3816/"
echo $string | awk -F"/" '{print ,,}' OFS="/"

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/"

как вы можете видеть, это гораздо более гибкий подход.

(все заслуги перед Ди)

sed 's|(http:\/\/[^\/]+\/).*||'

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

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

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

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

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

-e "s,$,/,"

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

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

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

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

это как надежно сделать не жадное сопоставление многозначных строк с помощью 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
echo "/home/one/two/three/myfile.txt" | sed 's|\(.*\)/.*||'

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.