Jak mogę wyodrębnić / przeanalizować pełny adres URL z pół losowego ciągu?

12

Chciałbym, aby bash parsował / wyodrębniał pełny adres URL (i tylko adres URL) z losowego krótkiego ciągu.

Przykłady:

bob, the address is http://www.google.com

lub

https://foo.com/category/example.html is up

lub

Error 123 occurred at http://bit.ly/~1223456677878

lub

Stats are up: https://foo1234.net/report.jpg

Próbowałem użyć, cat foo_output | egrep -o "https?://[\w'-\.]*\s"ale to nie działało.

Mike B.
źródło
Brzmi przerażająco, w zależności od tego, co chcesz zrobić z wyodrębnionym adresem URL ...
vonbrand

Odpowiedzi:

24

Próbowałeś:

egrep -o 'https?://[^ ]+' foo_output

zamiast?

Zauważ, że wszystko z klasą znaków jest traktowane jako dosłowne, więc powiedzenie [\w]nie pasuje do znaku słowa . Co więcej, nie musisz uciekać przed metaznakiem wyrażenia regularnego w klasie postaci, tzn. Powiedzenie [\.]to nie to samo, co [.].

diabelnie
źródło
2
[^ ]jest zbyt szeroki, będziemy chcieli, aby wykluczyć inne półfabrykaty, (, ), ewentualnie comas, i wszystkie znaki, które nie są dozwolone w adresach URL.
Stéphane Chazelas
@StephaneChazelas Masz rację. Zakładałem jednak, że adres URL jest poprzedzony spacją, chyba że na początku lub na końcu wiersza.
devnull
5

Identyfikatory URI nie są dobrze dopasowane do dopasowywania wyrażeń regularnych, gdy są osadzone w języku naturalnym. Jednak obecny stan techniki to Improved Liberal, Accurate Regex Pattern for Matching URLs Johna Grubera . Jak obecnie pisano, wersja jednowierszowa wygląda następująco:

(?i)\b((?:[a-z][\w-]+:(?:/{1,3}|[a-z0-9%])|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’]))

John wydaje się również utrzymywać tutaj sedno , chociaż jego wpis na blogu znacznie lepiej tłumaczy jego testowy korpus i ograniczenia wzorca wyrażeń regularnych.

Jeśli chcesz zaimplementować wyrażenie z wiersza poleceń, możesz być ograniczony przez używany silnik wyrażeń regularnych lub przez problemy z cytowaniem powłoki. Odkryłem, że skrypt Ruby jest najlepszą opcją, ale przebieg może się różnić.

CodeGnome
źródło
2
Proszę dołączyć regex w swojej odpowiedzi zamiast linku do niego.
terdon
@terdon, pełne wyrażenie regularne to około 60 linii.
vonbrand
2
@ vonbrand Wiem, widziałem to. Staramy się po prostu unikać łączenia z zasobami zewnętrznymi. Istotą witryn SE jest wiki. Co się stanie, jeśli blog, do którego prowadzisz link, przejdzie w tryb offline? Twoja odpowiedź stanie się bezużyteczna. W każdym razie 60 linii to niewiele, a czytelność to tylko 60 linii.
terdon
2

Problem z dopasowaniem adresów URL polega na tym, że w adresie URL może znajdować się prawie wszystko :

https://encrypted.google.com/search?hl=en&q=foo#hl=en&q=foo&tbs=qdr:w,sbd:1

Jak widać, The (prawidłowy) URL powyżej zawiera $, ?, #, &, ,, .i :. Zasadniczo jedyne, co możesz mieć pewność, że adres URL nie zawiera, to puste miejsce. Mając to na uwadze, możesz wyodrębnić adresy URL za pomocą tak prostego wzorca, jak:

$ grep -oP 'http.?://\S+' file 
http://www.google.com
https://foo.com/category/example.html
http://bit.ly/~1223456677878
https://foo1234.net/report.jpg

\SMecze żadnych non-space znaków w Perl wyrażeń regularnych kompatybilny (PCREs), przy czym -Paktywuje PCREs za grepi -oczyni go wydrukować tylko dopasowany segment linii.

terdon
źródło
0

Wybrałbym łańcuchy, ale trochę inaczej. Jeśli masz fragment tekstu podobny do twojego w pliku tekstowym o nazwie strings.txt, możesz wykonać następujące czynności:

grep http ./strings.txt | sed 's/http/\nhttp/g' | grep ^http | sed 's/\(^http[^ <]*\)\(.*\)/\1/g' | grep IWANTthis | sort -u

Wyjaśnienie:

grep http ./st3.txt      => will catch lines with http from text file
sed 's/http/\nhttp/g'    => will insert newline before each http
grep ^http               => will take only lines starting with http
sed 's/\(^http[^ <]*\)\(.*\)/\1/g'   
                         => will preserve string from ^http until first space or < (the latter in hope if 
grep IWANTthis           => will take only urls containing your text of your interest; you can omit this.
sort -u                  => will sort the list and remove duplicates from it 

Ponieważ istnieje szansa, że ​​adres URL może nie działać, możesz wykonać dodatkowe sprawdzanie błędów za pomocą interesującego adresu URL. np. wget -p URL -O /dev/null- wypisze całkiem inne kody błędów w przypadku, gdy adres URL nie jest dostępny, więc można skonfigurować pętlę do przetwarzania listy linków i wyświetlania ich statusu ważności.

Jeśli ostatecznie wyodrębniasz linki z plików HTML, mogą wystąpić problemy sedw szczególnych przypadkach. Jak zasugerowano w zabawnym (postie) , który prawdopodobnie już widziałeś - najlepiej nie używać wyrażeń regularnych, ale silnik parsera HTML. Jednym z takich łatwo dostępnych parserów jest przeglądarka tekstowa lynx(dostępna w dowolnym systemie Linux). Pozwala to na natychmiastowe zrzucenie listy wszystkich linków w pliku, a następnie wyodrębnienie adresów URL za pomocą grep.

lynx -dump -listonly myhtmlfile.html | grep IWANTthisString | sort -u

Nie zadziała to jednak w przypadku większości zniekształconych plików HTML lub fragmentów tekstu z łączami.

r0berts
źródło
-1

Właśnie egrep -o 'https?://[^ ")]+'

które będą zawierać url()i „http”

Roberto Bertó
źródło
3
Czym różni się to od odpowiedzi Devnull? Mam nadzieję, że zdajesz sobie sprawę, że użycie egrepjest przestarzałe.
Anthon
Jeśli masz poprawę w stosunku do istniejącej odpowiedzi, możesz powrócić do niej za pomocą linku „udostępnij” pod tą odpowiedzią. Zobacz także strony pomocy
Jeff Schaller
-1
cat text-file.txt | grep -Eo '(https?|ftp|file)://[-A-Za-z0-9\+&@#/%?=~_|!:,.;]*[-A-Za-z0-9\+&@#/%=~_|]'

alternatywnie dodaj polecenie SED, aby zapisać go w pliku CSV:

| sed 's/;/<tab>/g' > file.csv
MakoBuk
źródło