2012-03-13 12 views
13

Mam plik, który przechowuje dane o numerze telefonu, a także kilka bezużytecznych rzeczy. Próbuję zanalizować numery, a gdy jest tylko jeden numer telefonu/linia, nie jest to problem. Ale kiedy mam wiele numerów, sed pasuje do ostatniego (mimo że wszędzie mówi, że powinien pasować tylko do pierwszego wzorca?), I nie mogę uzyskać innych numerów.SED: wiele wzorów na tej samej linii, jak dopasować/przeanalizować pierwszy

My data.txt:

bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla 

Kiedy przetwarza na dane, mój pomysł był pierwszy, aby usunąć wszystkie „początkowy” „bla bla bla” przed numerem pierwszego telefonu (więc szukać pierwszego wystąpienia „NUM:”) , a następnie usuwam wszystkie rzeczy po numerze telefonu i otrzymuję numer. Po tym chcę przeanalizować następne wystąpienie z pozostałego ciągu.

Więc teraz, gdy próbuję go sed, zawsze dotrzesz do ostatniego numeru na linii:

>sed 's/.*NUM://' data.txt 
08022222222 bla bla bla 
> 

Przede wszystkim chciałbym, aby zrozumieć, co się dzieje z moim rozumieniem SED. Oczywiście bardziej skuteczne sugestie są mile widziane! Czy moje polecenie sed nie mówi, zamień wszystkie rzeczy przed 'NUM:' na '' (puste)? Dlaczego pasuje zawsze do ostatniego wystąpienia?

Dzięki!

+3

Sed jest chciwy.Jeśli istnieje drugi NUM :, pierwszy może być spożyty przez '. *'. –

+0

+1 dla danych przykładowych, dorozumianych oczekiwanych wyników i niektórych przykładowy kod, który nie działa. Powodzenia. – shellter

Odpowiedz

19

To może pracować dla Ciebie:

echo "bla bla bla NUM:09011111111 bla bla bla bla NUM:08022222222 bla bla bla" | 
sed 's/NUM:/\n&/g;s/[^\n]*\n\(NUM:[0-9]*\)[^\n]*/\1 /g;s/.$//' 
NUM:09011111111 NUM:08022222222 

Problem masz jest zrozumienie, że .* jest chciwy czyli pasuje najdłuższy mecz nie pierwszy mecz. Umieszczając unikatową postać (\n sed używa jej jako ogranicznika linii, więc nie może istnieć w linii) przed ciągiem, który nas interesuje (NUM:...) i usuwając wszystko, co nie jest unikatowym znakiem [^\n]*, po którym następuje unikatowa postać \n, skutecznie dzielimy łańcuch na łatwe do zarządzania elementy.

+1

Podejrzewałem miało to jakiś związek z chciwością, – julumme

+0

Wow, która odpowiada na moje długie poszukiwania przykładu postaci. w przeciwieństwie do liniowej pracy sed. Widzę, że umieszczamy znak nowej linii jako znacznik w przestrzeni wzorca opartego na liniach, a następnie usuwamy części, które kończą się tym znacznikiem, aby przeciwdziałać chciwemu dopasowaniu sed. –

+0

OSX: '\ n' nie działa dla sed. Zamiast tego użyj "gsed" (do zainstalowania z Brew). – ericpeters0n

0

Można użyć tego wzoru:

sed -r 's/^(.*NUM:)(.*NUM:.*)$/\2/' 
11

Jak już wiecie, wyrazy regularne są chciwe i o ile mogę powiedzieć, nie można uczynić nie chciwymi.

Dwie alternatywy, które do tej pory nie były rozwijane, to użycie innych narzędzi do tego typu dopasowywania/wyodrębniania.

Możesz użyć perl jako zamiennik dla sed z parametrami -pe.Wspiera ? non-chciwy modyfikator:

$ perl -pe 's/.*?NUM://' data.txt 
09011111111 bla bla bla bla NUM:08022222222 bla bla bla 

Można skorzystać z możliwości GNU grep -o dostać tylko fragmenty swoich danych, które pasują do wyrażenia regularnego:

$ egrep -o 'NUM:[0-9]*' data.txt 
NUM:09011111111 
NUM:08022222222 
+0

Dziękuję za sugestię alternatywy, na pewno przyjrzę się możliwym różnicom w wydajności między sed i perl – julumme

+0

Dzięki za propozycję egrep. Szkoda, że ​​sed ogranicza się do przestrzeni wzorów zajmujących całe linie. –

2

Jeśli numer jest zdefiniowana przez cyfr następstwie NUM::

sed -n -e 's/$/\n/' -e ':begin' \ 
    -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \ 
    -e 'tbegin' -e 's/.*\n //' -e '/NUM/p' 

Co to robi to:

  1. Umieść \n na końcu linii, aby działać jak znacznik.
  2. Spróbuj znaleźć numer przed markerem i umieść go na końcu linii (za znacznikiem).
  3. Jeśli znaleziono numer, musisz 2 powyżej.
  4. Jeśli przed markerem nie pozostanie żadna liczba, usuń wszystko przed liczbami.
  5. Jeśli numer jest na linii, wydrukować (obsłużyć przypadek, gdy numer nie jest znaleziony

Można również zrobić na odwrót, najpierw spada linie bez numerów.

sed -e '/NUM/!d' -e 's/$/\n/' -e ':begin' \ 
    -e 's/\(NUM:[0-9][0-9]*\)\(.*\)\n\(.*\)/\2\n\3 \1/' \ 
    -e 'tbegin' -e 's/.*\n //' 
+0

Doceniam, że poświęciłeś czas, aby dać mi alternatywne rozwiązanie, ja to zbadam. Jednak wydaje się to nieco trudne do zrozumienia, a także istnieje wiele połączeń do sed tutaj, obawiam się, że wydajność jest wolniejsza niż w "3-call solution" – julumme

+0

Istnieje tylko jedno połączenie "sed", tylko trochę bardziej złożony skrypt z 6 komendami. Masz rację, rozwiązanie potonga ma tylko 3 polecenia, ale te polecenia są wykonywane więcej niż jeden raz (argument 'g' polecenia' s'), więc nie oznacza to, że jest on szybszy. Zgadzam się, że ten problem jest nieco bardziej elegancki. – jfg956

Powiązane problemy