2009-12-07 13 views
9

Jak używać polecenia grep do wyświetlania wystąpień ciągu "eksportuj do programu Excel" w plikach wejściowych podanych poniżej? Konkretnie, jak obsługiwać podziały wierszy, które występują między ciągami wyszukiwania? Czy jest jakiś przełącznik w grep, który może to zrobić, czy jakieś inne polecenie?Łańcuchy wyszukiwania Grep z podziałem wierszy

pliki wejściowe:

a.txt Plik:

bla bla ... Eksport do
Excel ...
bla bla ..

pliku b. txt:

bla bla. .. eksport do Excela ...
bla bla ..

+0

Jak rozumiem (referencje: Unix Power Tools) rodzina programów grep jest zorientowana liniowo, czyta linię po linii i dlatego nie może znaleźć wzorów wzdłuż linii. Więc możesz wymyślić skrypt perla lub użyć sed tutaj. HTH. – sateesh

+0

jak korzystać z sed w tym kontekście? –

+0

@Vijay: echo -e "foo \ nbar" | sed -n 'N;/foo \ nbar/p' – SiegeX

Odpowiedz

6

Czy po prostu chcesz znaleźć pliki, które zawierają wzorzec, ignorując linebreaks, czy chcesz, aby rzeczywiście zobaczyć pasujących linii?

W pierwszym przypadku można użyć tr przekonwertować znaki nowej linii do pomieszczeń:

tr '\n' ' ' | grep 'export to excel' 

Jeśli to drugie można zrobić to samo, ale może chcesz użyć flagi -o aby drukować tylko rzeczywista mecz. Będziesz wtedy chciał dostosować swoje wyrażenie regularne tak, aby zawierał dodatkowy kontekst, który chcesz.

+3

Tr + grep rozwiązanie nie bardzo nadaje się do dużych plików, ponieważ zamierza utworzyć jeden duży ciąg. – ghostdog74

0

używać gawk. ustaw separator rekordów jako excel, a następnie sprawdź "eksportuj do".

gawk -vRS="excel" '/export.*to/{print "found export to excel at record: "NR}' file 

lub

gawk '/export.*to.*excel/{print} 
/export to/&&!/excel/{ 
    s=$0 
    getline line 
    if (line~/excel/){ 
    printf "%s\n%s\n",s,line 
    } 
}' file 
+0

W jaki sposób wydrukowałbyś rzeczywiste wiersze jako 'grep' (w przypadku dopasowań w ramach jego możliwości)? –

+0

wydrukuj rekord, 0 USD. W przeciwnym razie nie rozumiem, co masz na myśli. – ghostdog74

+0

Myślę, że twoja edycja się tym zajmuje. Jednak w niektórych przypadkach nie działa. Jeśli wprowadzono coś takiego jak "eksport excel do \ nexcel" lub "eksport do \ n coś innego niż excel", na przykład. Aby odpowiedzieć na twoje pytanie w komentarzu: oryginalna jedna liniówka, jeśli do wyjścia dodano 0 USD, nie wyświetliłaby słowa "excel", a zwłaszcza "..." po tym, co jest wskazane w pytaniu OP. –

0

Ja testowałem to trochę i wydaje się działać:

sed -n '$b; /export to excel/{p; b}; N; /export to\nexcel/{p; b}; D' filename 

Można zezwolić na dodatkowe białe miejsca na końcu i na początku linii tak:

sed -n '$b; /export to excel/{p; b}; N; /export to\s*\n\s*excel/{p; b}; D' filename 
2

Nie wiem, jak to zrobić w grep. Sprawdziłem stronę man dla egrep(1) i nie może ona być zgodna z nowym znakiem w środku.

Podoba mi się rozwiązanie @Laurence Gonsalves zasugerował, aby użyć tr(1) do usunięcia nowych linii. Ale jak zauważył, będzie to trudny wydruk pasujących linii, jeśli zrobisz to w ten sposób.

Jeśli chcesz dopasować pomimo nowego wiersza, a następnie wydrukować pasujące wiersze, nie mogę wymyślić sposobu, aby to zrobić z grep, ale nie byłoby to zbyt trudne w żadnym z Python, AWK, Perl lub Ruby.

Oto skrypt w języku Python, który rozwiązuje problem. Zdecydowałem, że w przypadku linii, które pasują tylko po połączeniu z poprzednią linią, wydrukowałbym strzałkę --> przed drugą linią meczu. Linie, które pasują wprost, są zawsze drukowane bez strzałki.

Zostało to napisane przy założeniu, że/usr/bin/python to Python 2.x.W razie potrzeby możesz w trywialny sposób zmienić skrypt tak, by działał w Pythonie 3.x.

#!/usr/bin/python 

import re 
import sys 

s_pat = "export\s+to\s+excel" 
pat = re.compile(s_pat) 

def print_ete(fname): 
    try: 
     f = open(fname, "rt") 
    except IOError: 
     sys.stderr.write('print_ete: unable to open file "%s"\n' % fname) 
     sys.exit(2) 

    prev_line = "" 
    i_last = -10 
    for i, line in enumerate(f): 
     # is ete within current line? 
     if pat.search(line): 
      print "%s:%d: %s" % (fname, i+1, line.strip()) 
      i_last = i 
     else: 
      # construct extended line that included previous 
      # note newline is stripped 
      s = prev_line.strip("\n") + " " + line 
      # is ete within extended line? 
      if pat.search(s): 
       # matched ete in extended so want both lines printed 
       # did we print prev line? 
       if not i_last == (i - 1): 
        # no so print it now 
        print "%s:%d: %s" % (fname, i, prev_line.strip()) 
       # print cur line with special marker 
       print "--> %s:%d: %s" % (fname, i+1, line.strip()) 
       i_last = i 
     # make sure we don't match ete twice 
     prev_line = re.sub(pat, "", line) 

try: 
    if sys.argv[1] in ("-h", "--help"): 
     raise IndexError # print help 
except IndexError: 
    sys.stderr.write("print_ete <filename>\n") 
    sys.stderr.write('grep-like tool to print lines matching "%s"\n' % 
      "export to excel") 
    sys.exit(1) 

print_ete(sys.argv[1]) 

EDYCJA: dodano komentarze.

Poszedłem do kłopotów, aby wydrukować prawidłowy numer linii w każdym wierszu, używając formatu podobnego do tego, co otrzymasz z grep -Hn.

To może być dużo krótsza i prostsza, jeśli nie trzeba numery linii, i nie przeszkadza w czytaniu całego pliku od razu do pamięci:

#!/usr/bin/python 

import re 
import sys 

# This pattern not compiled with re.MULTILINE on purpose. 
# We *want* the \s pattern to match a newline here so it can 
# match across multiple lines. 
# Note the match group that gathers text around ete pattern uses a character 
# class that matches anything but "\n", to grab text around ete. 
s_pat = "([^\n]*export\s+to\s+excel[^\n]*)" 
pat = re.compile(s_pat) 

def print_ete(fname): 
    try: 
     text = open(fname, "rt").read() 
    except IOError: 
     sys.stderr.write('print_ete: unable to open file "%s"\n' % fname) 
     sys.exit(2) 

    for s_match in re.findall(pat, text): 
     print s_match 

try: 
    if sys.argv[1] in ("-h", "--help"): 
     raise IndexError # print help 
except IndexError: 
    sys.stderr.write("print_ete <filename>\n") 
    sys.stderr.write('grep-like tool to print lines matching "%s"\n' % 
      "export to excel") 
    sys.exit(1) 

print_ete(sys.argv[1]) 
+0

Nie widzę, żebyś skompilował regex z re.MULTILINE, więc jak sprawdza "excel" w innym wierszu? – ghostdog74

+0

re.MULTILINE był * nie * czym chciałem, więc nie określiłem go. Z re.MULTILINE, kod 're' traktuje znak nowej linii, taki jak koniec łańcucha, i nie pasuje po nim. Chciałem, aby znak nowej linii był traktowany jak każda inna biała przestrzeń w dopasowaniu. Dodam kilka komentarzy do kodu. – steveha

+0

W rzeczywistości moja pierwsza wersja działałaby tak samo z re .MULTILINE lub bez niego. Druga wersja pliku w wersji "wszystko w całość" nie musi mieć tej flagi, ponieważ zależy od dopasowania wokół nowego wiersza. Pierwsza wersja buduje specjalną pojedynczą linię i usuwa wszelkie nowe linie w procesie. – steveha

1

grep -A1 „eksportu do” nazwa pliku grep -B1 "excel"

+2

To rozwiązanie nie zapewnia, że ​​"eksport do" jest obok "excel". Będzie pasować, na przykład, "eksport do \ nblah blah blah blah excel". – stepthom

+0

Nie pasuje również do "eksportu \ ndo excel" i nie skaluje się do wyszukiwania ciągu zawierającego wiele spacji. – Keelan

Powiązane problemy