2009-12-07 13 views
9

Come utilizzare grep per visualizzare le occorrenze della stringa "esporta in Excel" nei file di input indicati di seguito? In particolare, come gestire le interruzioni di riga che si verificano tra le stringhe di ricerca? C'è un interruttore in grep che può fare questo o qualche altro comando probabilmente?Stringhe di ricerca di grep con interruzioni di riga

I file di input:

File A.txt:

bla bla ... esportazione di
excel ...
bla bla ..

file b. txt:

blah blah. .. esportazione in Excel ...
bla bla ..

+0

Come ho capito (riferimento: Unix Power Tools) la famiglia di programmi grep è orientata alla linea, legge una riga alla volta e quindi non riesce a trovare pattern su tutta la linea. Quindi puoi pensare a uno script perl o usare sed qui. HTH. – sateesh

+0

come usare sed in questo contesto? –

+0

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

risposta

6

Vuoi solo per trovare i file che contengono il modello, ignorando le interruzioni di linea, o vuoi vedere effettivamente le linee corrispondenti?

Nel primo caso, è possibile utilizzare per convertire tr a capo di spazi:

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

In quest'ultimo caso si può fare la stessa cosa, ma si consiglia di utilizzare il flag -o di stampare solo l'attuale incontro. Dovrai quindi regolare la tua espressione regolare per includere qualsiasi contesto aggiuntivo che desideri.

+3

La soluzione tr + grep non è particolarmente adatta per i file di grandi dimensioni poiché sta per formare una stringa BIG. – ghostdog74

0

usare gawk. imposta il separatore dei record come excel, quindi controlla "export to".

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

o

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

Come stampereste le linee attuali come farebbe 'grep' (per le corrispondenze all'interno della sua capacità)? –

+0

stampa il record, $ 0. Altrimenti, non capisco cosa intendi. – ghostdog74

+0

Penso che la tua modifica si occupi di ciò. Tuttavia, fallisce per alcuni casi limite. Ad esempio, se l'input fosse qualcosa come "excel export to \ nexcel" o "export to \ nsomething diverso da excel". Per rispondere alla tua domanda nel tuo commento: l'originale one-liner, se $ 0 sono stati aggiunti all'output, non mostrerebbe "excel" e in particolare il "..." dopo quello indicato nella domanda dell'OP. –

0

Ho testato questo un po 'e sembra funzionare:

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

È possibile autorizzare per un po' di spazio bianco extra alla fine e l'inizio delle linee così:

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

Non so come farlo in grep. Ho controllato la pagina man per egrep(1) e non può corrispondere con una nuova riga nel mezzo.

Mi piace la soluzione suggerita da Laurence Gonsalves, di utilizzare tr(1) per eliminare le nuove righe. Ma come ha notato, sarà un problema stampare le righe corrispondenti se lo fai in questo modo.

Se si vuole uguagliare nonostante una nuova riga e quindi stampare la/e linea/e corrispondente/i, non riesco a pensare a un modo per farlo con grep, ma non sarebbe troppo difficile in nessuno dei Python, AWK, Perl o Ruby.

Ecco uno script Python che risolve il problema. Ho deciso che, per le linee che corrispondono solo se collegate alla riga precedente, stamperei una freccia --> prima della seconda riga della partita. Le linee che si adattano a titolo definitivo vengono sempre stampate senza la freccia.

Questo è scritto supponendo che/usr/bin/python sia Python 2.x.È possibile cambiare banalmente lo script in modo che funzioni in Python 3.x se lo si desidera.

#!/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]) 

MODIFICA: commenti aggiunti.

Sono andato a qualche problema per farlo stampare il numero di linea corretto su ogni riga, utilizzando un formato simile a quello che si otterrebbe con grep -Hn.

Potrebbe essere molto più breve e più semplice se non hai bisogno di numeri di riga, e non ti dispiace la lettura in tutto il file in una sola volta nella memoria:

#!/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

non ti vedo compilato il regex con re.MULTILINE, quindi come controlla "excel" su un'altra riga? – ghostdog74

+0

re.MULTILINE era * non * quello che volevo, quindi non l'ho specificato. Con re.MULTILINE, il codice 're' tratta una nuova riga come la fine di una stringa, e non corrisponde dopo. Volevo una nuova riga trattata come qualsiasi altro spazio bianco nella corrispondenza. Aggiungerò alcuni commenti al codice. – steveha

+0

In realtà, la mia prima versione funzionerebbe allo stesso modo con o senza il re.MULTILINE. La seconda versione di slurp-in-whole-file non deve avere quel flag perché dipende dalla corrispondenza attorno a una nuova riga. La prima versione crea una linea singola speciale e rimuove qualsiasi newline nel processo. – steveha

1

grep -A1 "esportazione" nomefile | grep -B1 "excel"

+2

Questa soluzione non garantisce che "esporta in" sia accanto a "excel". Ad esempio, "esporterà a \ nblah blah blah blah excel". – stepthom

+0

Inoltre, non corrisponde a "export \ nto excel" e non viene ridimensionato alla ricerca di una stringa che contiene molti spazi. – Keelan