Nowe posty

xx Problem ze sterownikami. (5)
2024-04-13, 21:25:16
xx Instalacja xfce4 (2)
2024-04-13, 16:20:17
xx Serie kompilacji bez instalacji dla “emerge” w Gentoo (2)
2024-04-08, 18:40:04
xx Plasma 6 w Neonie ssie trochę mniej ... (17)
2024-04-05, 10:03:46
xx Problem z Linux Lite po instalacji (3)
2024-04-03, 14:23:40
xx Jak właczyć num locka przy starcie systemu debian 12? (12)
2024-04-02, 17:43:54
xx Brak dźwieku w systemie. (5)
2024-04-02, 16:13:41
xx Dystrybucja pod HP Omen (7)
2024-03-29, 11:33:05
xx [Poradnik] Wyszukiwanie Sterowników (2)
2024-03-27, 21:08:23
xx Ile pingwinów? (1)
2024-03-27, 08:59:24

Autor Wątek: regexp leniwy działający w jednej linii (a nie linia po linii)  (Przeczytany 3674 razy)

addos2

  • Gość
Zawartość 2 plików tekstowych to coś w rodzaju (różnica:  w nr 3 zamiast html jest php) :

plik_1
katalog/a1.html     AAAA      katalog/a2.html      AAAA      katalog/a3.html      AAAA      katalog/a4.html      AAAA
plik_2
katalog/a1.html     AAAA      katalog/a2.html      AAAA      katalog/a3.php       AAAA      katalog/a4.html      AAAA
Ważne jest to, że tekst jest w jednym wierszu, bo jeżeli te 4 wpisy są w osobnych liniach to całość działa poprawnie i nie zakładałbym nowego wątku

Wynik jaki chcę uzyskać ma być taki:

Dla pliku plik_1:
katalog/a1.html
katalog/a2.html
katalog/a3.html
katalog/a4.html

Dla pliku plik_2:
katalog/a1.html
katalog/a2.html
katalog/a4.html

Wydawać by sie mogło, że taka mała różnica pomiędzy plikami nie wpłynie na działanie programu grep.... a jednak  :o

Różne kombinacje jakie zastosowałem na tych 2-óch plikach:

Te 3 poniższe:
$ grep -Eo "katalog.*(html)" plik_1
$ grep -Eo "katalog.*?(html)" plik_1
$ grep -Po "katalog.*(html)" plik_1
dają ten sam niepoprawny efekt, czyli:
katalog/a1.html     AAAA      katalog/a2.html      AAAA      katalog/a3.html      AAAA      katalog/a4.html

Ten:
$ grep -Po "katalog.*?(html)" plik_1
daje wynik inny niż te 3 poprzednie:
katalog/a1.html
katalog/a2.html
katalog/a3.html
katalog/a4.html
... i o to mi właśnie chodziło, czyli ten wynik jest poprawny :)

Za to w przypadku pliku nr 2 jest już zgrzyt:

Pierwsze 3 polecenia:
$ grep -Eo "katalog.*(html)" plik_2
$ grep -Eo "katalog.*?(html)" plik_2
$ grep -Po "katalog.*(html)" plik_2
... dają niepoprawny wynik (tego się w sumie spodziewałem):
katalog/a1.html     AAAA      katalog/a2.html      AAAA      katalog/a3.php      AAAA      katalog/a4.html

Natomiast 4 polecenie:
$ grep -Po "katalog.*?(html)" plik_2
... z niewiadomych mi względów łączy nr 3 z nr 4:
katalog/a1.html
katalog/a2.html
katalog/a3.php      AAAA      katalog/a4.html

Czy ktoś wiem czemu?
Czy ma ktoś pomysł jak uzyskać tutaj wpisy bez tego z rozszerzeniem php? Mam na myśli taki efekt:
katalog/a1.html
katalog/a2.html
katalog/a4.html

Przypominam: Ważne jest to, że tekst jest w jednym wierszu, bo jeżeli te 4 wpisy są w osobnych liniach to całość działa poprawnie i nie zakładałbym nowego wątku

Offline 1709

  • Users
  • Guru
  • *****
  • Wiadomości: 2765
  • 1709
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #1 dnia: 2015-10-24, 07:51:06 »
 grep -Po "katalog.*?(html|php)" 1
$ grep -Po "katalog.*?(html|php)" 2
katalog/a1.html
katalog/a2.html
katalog/a3.php
katalog/a4.html
[gg@localhost t]$ grep -Po "katalog.*?(html|php)" 1
katalog/a1.html
katalog/a2.html
katalog/a3.html
katalog/a4.html
Może być?
"html|php " to oznacza html albo php.
PS: Brak polskiej czcionki, nie jest to brak lenistwa, a jej brak w systemie i brak czasu na reczne poprawki.

addos2

  • Gość
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #2 dnia: 2015-10-24, 10:48:31 »
OK, mój błąd w ostatnim zdaniu zdaniu i pytaniu.
Nie chodziło mi o to czemu to się łączy razem tylko jakim poleceniem uzyskać wszystkie wpisy z rozszerzeniem html, a resztę np. php olać.

Krótko mówiąc: chcę uzyskać wykaz tylko i wyłącznie wszystkiego co ma rozszerzenie html.

Poprawiłem ten niefortunny fragment

Offline 1709

  • Users
  • Guru
  • *****
  • Wiadomości: 2765
  • 1709
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #3 dnia: 2015-10-24, 12:31:40 »
Możesz zrobić to poprostu tak
$ grep -Po "katalog.*?(html|php)" 1 | grep html
katalog/a1.html
katalog/a2.html
katalog/a3.html
katalog/a4.html
[gg@localhost t]$ grep -Po "katalog.*?(html|php)" 2 | grep html
katalog/a1.html
katalog/a2.html
katalog/a4.html
Znaczek ten " | grep .... " w tym przypadku to potok czyli przekierowanie do następnej komendy.

Możesz też zrobić tak
$ grep -Po "katalog.*?(html|php)" 2 | grep -v php
katalog/a1.html
katalog/a2.html
katalog/a4.html
grep --help
...
 -v, --invert-match        select non-matching lines
"grep -v" zamiast wyszukiwać słowo to wyszukuje wszystko tylko nie te słowo, czyli przeciwieństwo.

Edit:
Jeszcze trochę inaczej
$ cat 2 | sed -e 's/katalog*/\nkatalog/2g' | cut -d" " -f1 | grep html
katalog/a1.html
katalog/a2.html
katalog/a4.html
sed wstawia nową linię, a cut wyraz odziela " " spacją i pokazuje tylko pierwsze słowo.

PS: Brak polskiej czcionki, nie jest to brak lenistwa, a jej brak w systemie i brak czasu na reczne poprawki.

addos2

  • Gość
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #4 dnia: 2015-10-24, 13:06:49 »
Działa ale ..... jak sobie pomyślę ile bym musiał wpisać wszystkich znanych mi rozszerzeń plików żeby odfiltrować tylko html to zgroza :)
Powyższe html i php to tylko przykłady. W rzeczywistości tych rozszerzeń mam o wiele wiele więcej i nawet nie za bardzo wiem co tam się codziennie może trafić :)
Dlatego nie mogę tego odfiltrować negacją -v

To musi działać tak:
1. znajdź słowo "katalog"
2. zbieraj kolejne znaki
3. jeżeli po drodze znowu znalazłeś słowo "katalog" - wtedy zapomnij wszystkie znaki jakie zebrałeś i wróć do punktu nr 2
4. jeżeli po drodze znalazłeś słowo "html" - wyświetl wynik, potem wyzeruj zapamiętane znaki, a następnie w tej samej linii będziesz szukał dalej czyli wróć do punktu nr 1
5. i tak aż do końca pliku

Wyobraź sobie taką zawartość:

plik_1
abc12-abc34-abcde12
plik_2
abc12-abcde34-abcde12
Wyświetl wszystkie fragmenty zaczynające się od "abc", potem nieważne co jest w środku, a kończy się to "12"
Dla pliku nr 1 działa, ale dla pliku nr 2 już to nie chce działać.

Offline 1709

  • Users
  • Guru
  • *****
  • Wiadomości: 2765
  • 1709
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #5 dnia: 2015-10-24, 13:12:54 »
I można jeszcze np. tak
$ cat 2 | tr " " "\n" | grep html
katalog/a1.html
katalog/a2.html
katalog/a4.html
tr zamienia spacje na nowe linie, grep wyszukuje linie zawierające słowo tylko html

Edit:
A co do twojego przykładu w ostatnim poście, zakładając że to podałeś to wyrazy oddzielone spacją,
a nie ciąg znaków to
$ cat 2 | tr " " "\n" | grep ^abc | grep 12$
abc12-abcde34-abcde12
"^" wyszukuje początek , a "$" wyszukuje koniec.
http://kurslinux.ovh.org/02przeszukiwanie.php
PS: Brak polskiej czcionki, nie jest to brak lenistwa, a jej brak w systemie i brak czasu na reczne poprawki.

addos2

  • Gość
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #6 dnia: 2015-10-24, 13:23:02 »
Doceniam ale niektóre fragmenty mają w nazwie spacje więc nie mogę tego zrobić  ;D
Ty mi tu nie kombinuj :D tylko pomóż mi z tym:
abc12-abc34-abcde12
abc12-abcde34-abcde12
bo jak to opanuję to reszta będzie spoko :)

Aha... żeby cię nie korciło  :P Te myśliniki to tylko ułatwienie dla oka. Można przyjąć, że nie ma ich i całość w obydwu przypadkach to jeden ciąg znaków :)
abc12abc34abcde12
abc12abcde34abcde12

Teraz nie powinno korcić :P

Offline 1709

  • Users
  • Guru
  • *****
  • Wiadomości: 2765
  • 1709
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #7 dnia: 2015-10-24, 14:02:28 »
Zobacz coś takiego
$ cat 2 
abc12abc34abcde12 abc12abc34abcde12
$ cat 2 | grep -o 'abc[^12]*12' 
abc12
abc34abcde12
abc12
abc34abcde12
wyszukuje słowa z początkiem abc , i z końcówką 12, ale nie jestem pewien czy idealnie.
PS: Brak polskiej czcionki, nie jest to brak lenistwa, a jej brak w systemie i brak czasu na reczne poprawki.

addos2

  • Gość
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #8 dnia: 2015-10-24, 14:23:04 »
No nie bardzo ci to wyszło, bo miało być tylko to, co zaczyna się na "abc" ale jednocześnie nie ma w środku ponownie "abc".
To "abc" może wystąpić tylko na początku frazy ale nie w środku.

To tak jak masz "http://" i ta fraza może wystąpić tylko na początku, prawda?
Nie ma takich adresów jak: "http://cos-tam.pl/http://domena.pl"

A jeżeli masz kilka adresów pod rząd, to wygląda to tak:
http://domena.pl     http://domena.de    http://domena.pl
// Nie wiem czemu pierwsze http jest zaznaczone innym kolorem //

Przerób mi to polecenie tak, żeby w wyniku dały mi same domeny z końcówką pl: :)
echo "http://domena.plhttp://domena.dehttp://domena.pl" | grep -Po "http.*?(pl)"

Offline 1709

  • Users
  • Guru
  • *****
  • Wiadomości: 2765
  • 1709
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #9 dnia: 2015-10-24, 14:31:25 »
Tak, ale link nie może zawierać spacji, natomiast przed http musi wystąpić spacja
(lub nowa linia),
więc jesli chciałbym wyciągnąć tylko link to użyłbym komendy tr jak wyżej,
chociaż ja przy parsowaniu plików zazwyczaj używam tylko grep i cut.
Poza tym z tego wyniku możesz sobie użyć "grep -v" także.
Moment.

Edit:
$ echo "http://domena.plhttp://domena.dehttp://domena.pl" | sed -e 's/http\:\/\/*/ http\:\/\//g' | tr " " "\n" | grep .pl$
http://domena.pl
http://domena.pl
Edit:
Poprawiłem komende sed, bo o 1 za dużo dwukropków dałem, a potem jeszcze grep dodałem, bo niechcący usunełem.

Można odrazu dać /n
$ echo "http://domena.plhttp://domena.dehttp://domena.pl" | sed -e 's/http\:\/\/*/\nhttp\:\/\//g' | grep .pl$
http://domena.pl
http://domena.pl
Sed   " sed -e 's/ coś1 / coś2 /g' " zamienia wyżej "http://" na "\n http://" czyli z nową linią,
"*" dałem niepotrzebnie  ;D , i grep pokazuje tylko linie z końcówką ".pl"

Edit:
$ echo "http://domena.plhttp://domena.dehttp://domena.plhttp://domenahttp.pl" | grep -o 'http://[^pl]*pl' 
http://domena.pl
http://domena.pl

Edit:
No i jeszcze z adresem który twierdzisz że nie istnieje
$ echo "http://domena.plhttp://domena.dehttp://domena.plhttp://domenahttp.pl http://cos-tam.pl/http://domena.pl" | grep -o 'http://[^pl]*pl' 
http://domena.pl
http://domena.pl
http://cos-tam.pl
http://domena.pl
PS: Brak polskiej czcionki, nie jest to brak lenistwa, a jej brak w systemie i brak czasu na reczne poprawki.

Offline Paweł Kraszewski

  • Administrator
  • Guru
  • *****
  • Wiadomości: 3056
  • Lenistwo jest matką potrzeby = babcią wynalazku
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #10 dnia: 2015-10-24, 15:53:11 »
Strasznie podoba mi się próba zmuszenia GREPa do robienia czegoś, do czego nie został zaprojektowany - do pracy wewnątrz linii tekstu, nie na linii jako całości...

pawel@i7 ~/Pobrane > cat plik1.txt 
katalog/a1.html     AAAA      katalog/a2.html      AAAA      katalog/a3.html      AAAA      katalog/a4.html      AAAA

pawel@i7 ~/Pobrane > cat plik2.txt
katalog/a1.html     AAAA      katalog/a2.html      AAAA      katalog/a3.php       AAAA      katalog/a4.html      AAAA

pawel@i7 ~/Pobrane > awk 'BEGIN{RS="[ \t\n]"}/\.html$/{print}' plik1.txt
katalog/a1.html
katalog/a2.html
katalog/a3.html
katalog/a4.html

pawel@i7 ~/Pobrane > awk 'BEGIN{RS="[ \t\n]"}/\.html$/{print}' plik2.txt
katalog/a1.html
katalog/a2.html
katalog/a4.html


Wyjaśnienie:

1. Rozwinięty skrypt AWK:
BEGIN{
   RS="[ \t\n]"
}

/\.html$/ {
   print
}

AWK ma kilka magicznych zmiennych, wśród nich RS (od Record Separator), która zawiera wyrażenie regularne, które AWK potraktuje właściwie jako znak końca linii.

2. W klauzuli BEGIN, czyli przed analizą pliku ustawiamy RS na regekspa "któraś z białych spacji". Od tego momentu AWK traktuje każde słowo jako osobną linię do analizy.
3. Robimy klauzulę reagującą na regekspa \.html$, czyli to o co chodzi pytającemu. W klauzuli tej po prostu drukujemy pasujące wyrażenie poleceniem print.

Czyli AWK tnie cały tekst na wszelkich białych spacjach na wyrazy i każdy bada regekspem - jak pasuje, to wyświetla na ekranie.
« Ostatnia zmiana: 2015-10-24, 15:59:47 wysłana przez Paweł Kraszewski »
Paweł Kraszewski
~Arch/Void/Gentoo/FreeBSD/OpenBSD/Specjalizowane customy

xavery

  • Gość
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #11 dnia: 2015-10-30, 15:31:36 »
1. Rozwinięty skrypt AWK:
BEGIN{
   RS="[ \t\n]"
}

/\.html$/ {
   print
}


Trochę za bardzo rozwinięty:)

jak na te dane wejściowe to wystarczy:


awk '{print $1}'


względnie:


cut -f1 -d' '


Grepem oczywiście też da się to zrobić ale jak kolega wspomniał - są lepsze narzędzia i warto ich używać. [/code]
« Ostatnia zmiana: 2015-10-30, 15:33:25 wysłana przez Grzegorz Świtkowski »

Offline Paweł Kraszewski

  • Administrator
  • Guru
  • *****
  • Wiadomości: 3056
  • Lenistwo jest matką potrzeby = babcią wynalazku
    • Zobacz profil
Odp: regexp leniwy działający w jednej linii (a nie linia po linii)
« Odpowiedź #12 dnia: 2015-10-31, 05:39:50 »
Obejrzyj dane na szerszym monitorze :)
Paweł Kraszewski
~Arch/Void/Gentoo/FreeBSD/OpenBSD/Specjalizowane customy