Esiste un'alternativa agli switch -A -B -C di grep (per stampare poche righe prima e dopo)?


10
grep -A 2 -B 3 

stampa 2 righe dopo la stringa grep e stampa 3 righe prima.

grep -C 3

stampa 3 righe prima e 3 righe dopo

Sfortunatamente, grepsto usando non supporta queste opzioni. Sono disponibili comandi o script alternativi per simulare questo? Usando gli script sed/ awk/ perl/ shell?


+1 Non sapevo che ci fosse un -Cinterruttore.
Lazer,

1
Installa il grep di GNU. Più in generale, quando una nuova macchina Sun è arrivata qui, il primo passo nell'installazione è stato quello che qualcuno ha chiamato GNU > /usr/local. I programmi GNU hanno molte estensioni molto utili e sono progettati per evitare restrizioni arbitrarie (ma paghi caro in termini di dimensioni e talvolta prestazioni). Molti sistemi propietari hanno repository di pacchetti "non ufficiali" con GNU e altri strumenti. Il "partner" non ti parlerà di loro, anche quando sono gestiti dal venditore ...
vonbrand

Risposte:


6

Un modo moderatamente brutto per farlo è

grep -v pattern file >file.tmp; diff -c file.tmp file

o sostituire -ccon -C NUMper le NUMlinee di contesto. Produrrà un output extra, però. (Se diffsupporta -u/ -U NUM, sarà più pulito.)

Se il tuo diffnon ha -c/ -C/ -u, ci sono ancora modi per farlo, ma sono piuttosto brutti. D'altra parte, un sistema che diffnon supporta nemmeno -cprobabilmente non ha nemmeno Perl.


Questo è bello, funziona come il fascino, anche se ho dovuto usare l'opzione -bitw con questo per farlo funzionare per i file generati da Windows.
Prashant Bhate,

Puoi inviare stdin a diff e saltare il temporaneo:grep -v pattern file | diff -c - file
Cascabel,

5

ack richiede solo Perl, e comprende -A, -Be -Cle opzioni che funzionano come grep di. Usa la sintassi della regex di Perl invece di quella di grep, e il modo in cui seleziona i file da cercare è abbastanza diverso. Potresti voler provare l' -fopzione quando la usi (che stampa i file che cercherà senza effettivamente cercare nulla).

Può essere installato come un singolo script che non richiede moduli non core. Rilascialo nella tua ~/bindirectory (o in qualsiasi altro punto del PERCORSO a cui hai accesso in scrittura) e assicurati che sia chmodeseguibile.


La sua scatola di produzione e purtroppo non ho abbastanza privilegi per installare nulla, e non posso rischiare, tuttavia, grazie a questo consiglio lo installerò e proverò sul mio laptop di casa
Prashant Bhate,

@Prashant, non è necessario il root da installare ackper uso personale.
cjm,

Sì, ma non posso ancora usarlo lì, anche se è sicuro che questo script rimarrà per sempre nel mio ~ / bin :)
Prashant Bhate,

@Prashant: Perché non puoi usarlo? È solo uno script perl.
intuito il

1
La sua scatola di PRODUZIONE, deve prendere le autorizzazioni speciali autorizzazioni bla bla bla ... per fare qualsiasi cosa su di esso. e qualcosa va storto lì mi viene in mente;) e non ne vale la pena :)
Prashant Bhate

5

Questo semplice script perl emula grep -Ain una certa misura

#!/usr/bin/perl

$pattern=shift; #patthern to search
$lines=shift; # number of lines to print

$n = 0;
while (<>) {
  $n = $lines if /$pattern/; # reset counting
  if ($n) { print; $n-- } # print if within
  $n = 0 if eof; # don't leak across file boundaries
}

Si noti che è possibile aggiungere una dichiarazione di utilizzo per rendere leggibile e utilizzabile lo script;)

USAGE:    $./grep-A.pl <pattern> <numLines> <filename> 

Bene, quale versione di Perl mi serve per eseguire questo?
Prashant Bhate,

Uso v5.10.1, immagino che perl 5 sia abbastanza comune in questi giorni.
Vijay Anant,

è il 5.8.8 e funziona benissimo, ma ho bisogno di una sceneggiatura che faccia quello che -B fa
Prashant Bhate,

Buono. Vorrei cambiare l'ordine degli argomenti, però; grep-A 3 foosembra molto più naturale di grep-A foo 3. :-)
musiphil

3

Puoi semplicemente installare GNU grep o Ack (scritto in Perl, comprende molte delle opzioni di GNU grep e altro).

Se preferisci attenersi agli strumenti standard più un po 'di script, ecco uno script awk che emula il comportamento di grep -Ae -Bopzioni di GNU . Testato al minimo.

#!/bin/sh
# grep-ac: a grep-like awk script
# Arguments: pattern = awk regexp to search for
#            before = number of lines to print before a match
#            after = number of lines to print after a match
{ "exec" "awk" "-f" "$0" "$@"; }
# The array h contains the history of lines that haven't been printed
# but are eligible for being "before" lines.
# The variable until contains the number of the last "after" line to print.
match($0, pattern) {   # the current line matches
    for (i in h) {
        print h[i];    # print each remaining before line
        delete h[i];   # delete each line as it's printed
    }
    until=NR+after;    # record the last after line to print
}
{
    if (NR<=until) print $0;    # from a match to its last after line: print
    else h[NR]=$0;              # after that: save in history
    delete h[NR-before];        # remove line too old to be a before line
}
END {exit !until}               # exit status: 0 if there was a match, else 1

Esegui come grep-ac -vpattern=PATTERN -vbefore=NBEFORE -vafter=NAFTERdove PATTERNè il modello per cercare ( un'espressione regolare estesa con alcune aggiunte awk ), ed NBEFOREe NAFTERsono i numeri di linee per la stampa prima e dopo una partita, rispettivamente (inadempiente a 0). Esempio:

<input_file grep-ac -vbefore=2 -vpattern='foo *bar'

Qualsiasi soluzione che memorizza i dati in array è fuori discussione ... come ho già detto in precedenza, la dimensione del file è piuttosto grande e potrebbe fuoriuscire. Anche Awk su questo sistema non consente dimensioni del file superiori a 3000 byte.
Prashant Bhate,

2
@Prashant: non capisco le tue obiezioni. Questo script elimina le righe una volta che non sono idonee a essere righe precedenti. Non utilizza più memoria di quanto sia intrinsecamente necessario dati i requisiti, tranne per il fatto che awk può avere un overhead maggiore di un programma per scopi speciali (ma inferiore a Perl, che stai prendendo in considerazione). La dimensione totale del file è completamente irrilevante.
Gilles 'SO- smetti di essere malvagio' il

2
{ "exec" "awk" "-f" "$0" "$@"; }: un modo molto ingegnoso per aggirare le limitazioni nell'analisi delle linee di shebang.
dubiousjim,

2

Si scopre che è abbastanza difficile emulare -B, a causa dei problemi che sorgono quando ci sono linee corrispondenti che si susseguono direttamente. Questo praticamente non consente l'utilizzo di alcun tipo di scansione di file single-pass-through.

Me ne sono reso conto mentre giocavo con la seguente approssimazione:

perl -pe 'if(/search_term/) {print foreach @A; print ">"; $B=4}; shift @A if push(@A, $_)>7; $_ = "" unless ($B-- > 0);' target_file

Questo funzionerà approssimativamente correttamente come farebbe grep -A7 -B3, con l'avvertenza descritta nel primo paragrafo.

Una soluzione alternativa (anche a file singolo) a questo problema è utilizzare perl per alimentare una stringa di comando:

sed -n `perl -pe '$_=(/search_term/?sprintf("%d,%dp;", $.-3,$.+4):"")' file` file

oneliner piuttosto lungo, ma questo file è molto grande, quindi spingere le linee nell'array in questo caso è una cattiva idea, vero?
Prashant Bhate,

Il shift @A if push(@A,$_)>7;bit mantiene solo un array di dimensioni massime 7. (questo è il tuo parametro -A). La seconda opzione mantiene un file incredibilmente piccolo (basta eseguire il perl senza il livello esterno sed per vedere cosa viene generato lì), ma legge il file due volte.
user455,

0

Usando sedpuoi prima ottenere i numeri di riga delle linee corrispondenti, decrementare e incrementare un dato numero di riga in un whileciclo e poi usare sed -n "n1,n2p"per stampare le linee del contesto iniziale ( n1) e finale ( n2) (simile sedall'alternativa suggerita dall'utente 455). Tuttavia, molti processi di lettura possono causare un calo delle prestazioni.

edpuò fare direttamente riferimento alle linee precedenti e seguenti di una linea abbinata, ma non riesce se l'intervallo di linee specificato non esiste; ad esempio, la riga corrispondente corrisponde alla riga numero 2, ma devono essere stampate 5 righe pre-corrispondenza. Usarlo edè quindi necessario aggiungere un numero appropriato di righe (vuote) all'inizio e alla fine. (Per i file di grandi dimensioni edpotrebbe non essere lo strumento giusto, vedere: bfs - scanner di file di grandi dimensioni ).

# sample code to match lines with number 5 plus previous & following line
# (using Bash)
printf '%s\n' {1..20} > num.txt

# sed
sed -n '/5/=' num.txt | while read num; do
   n1=$((num - 1))
   n2=$((num + 1))
   [[ $n1 -lt 1 ]] && n1=1
   sed -n "${n1},${n2}p" num.txt
   echo --
done | sed -e '${/^--$/d;}'

# ed
cat <<-'EOF' | ed -s num.txt | sed -e $'N;N;a\\\n--' | sed -e '${/^--$/d;}'
H
0i
beginning: added line one
.
$a
end: added line one
.
,g/5/km\
'm-1,'m+1p
q
EOF
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.