Не жадное (неохотное) соответствие регулярных выражений в sed?
Я пытаюсь использовать sed для очистки строк URL-адресов, чтобы извлечь только домен..
Так с:
http://www.suepearson.co.uk/product/174/71/3816/
хочу:
(либо с косой чертой трейнинга, либо без нее, это не имеет значения)
Я пробовал:
sed 's|(http://.*?/).*||'
и (избегая не жадного квантора)
sed 's|(http://.*?/).*||'
но я не могу заставить не жадный Квантор работать, поэтому он всегда в конечном итоге соответствует всей строке.
20 ответов:
ни основное, ни расширенное регулярное выражение Posix/GNU не распознает не жадный Квантор; вам нужно более позднее регулярное выражение. К счастью, регулярное выражение Perl для этого контекста довольно легко получить:
perl -pe 's|(http://.*?/).*||'
С 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и все другие вкусы регулярных выражений!
поиск первого вхождения выражения:
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как это работает?
это регулярное выражение извлекает выгоду из чередования
|. В каждой позиции двигатель будет искать первую сторону чередования (наша цель), и если она не соответствует второй стороне чередования, которая имеет точку.соответствует следующему непосредственному символу.так как глобальный флаг установлен, двигатель пытается продолжайте сопоставлять символ за символом до конца входной строки или нашей цели. Как только первая и единственная группа захвата левой стороны чередования будет сопоставлена
(EXPRESSION)остальная часть линии потребляется немедленно, а также.*. Теперь мы сохраняем нашу ценность в первой группе захвата.POSIX BRE
регулярное выражение:
\(\(\(EXPRESSION\).*\)*.\)*Sed:
sed "s/\(\(\(EXPRESSION\).*\)*.\)*//"пример (поиск первой последовательности цифры):
$ sed "s/\(\(\([0-9]\{1,\}\).*\)*.\)*//" <<< "foo 12 bar 34"12это похоже на версию ERE, но без изменения. Вот и все. В каждой отдельной позиции двигатель пытается соответствовать цифре.
если он найден, другие следующие цифры потребляются и захватываются, а остальная часть строки немедленно сопоставляется в противном случае, так как
*означает больше или равно нулю он пропускает вторую группу захвата\(\([0-9]\{1,\}\).*\)*и прибывает в точку.чтобы соответствовать одному символу, и этот процесс продолжается.найти первое вхождение 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.затем результат передается во второе регулярное выражение
\(\(start.*\)*.\)*это то же самое, что и версия POSIX BRE выше. Он соответствует одному символу если начать разделительstartне соответствует в противном случае он соответствует и захватывает начальный разделитель и соответствует остальным символам.
прямой ответ на ваш вопрос
Используя подход #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-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выше:
s/@/@A/g; s/{/@B/g; s/}/@C/gпреобразование{и}чтобы заполнить строки, которые не могут существовать во входных данных, чтобы эти символы были доступны для преобразованияfooиbarдля.s/foo/{/g; s/bar/}/gпреобразованиеfooиbarдо{и}соответственноs/{[^{}]*}/<&>/gвыполняет операцию, которую мы хотим-преобразованиеfoo...barдо<foo...bar>s/}/bar/g; s/{/foo/gпреобразование{и}наfooиbar.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:
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