Grep rimuove la riga con 0 ma non con 0,2?


12

Ho un file il cui contenuto è simile al seguente.

0
0
0.2
0
0
0
0

Devo rimuovere tutte le righe con un singolo zero.
Stavo pensando di usare grep -v "0", ma questo rimuove anche la riga contenente 0,2. Ho visto che potrei usare l' -wopzione, ma questo non sembra funzionare neanche.

Come posso rimuovere tutte le righe contenenti solo un singolo 0 e mantenere tutte quelle righe che iniziano con uno 0?


2
Possibile duplicato della stringa esatta Match usando grep
Julien Lopez,

1
@JulienLopez Non è un inganno di quella domanda. Quella domanda riguarda la corrispondenza di una parola e la risposta -w, che qui fallisce.
Sparhawk,

Perché sei costretto a utilizzare grepper questo compito? E cosa intendi esattamente con un singolo zero ? Sembra molto un problema XY .
Roland Illig,

1
@RolandIllig era un'ora prima di coricarsi e volevo iniziare a elaborare una serie di 500.000 stringhe per verificare se fossero chiavi private bitcoin e in tal caso ottenere un equilibrio. La prossima volta che ho avuto il tempo di guardarlo, avevo elaborato molte migliaia di stringhe e volevo solo analizzare qualsiasi valore diverso da zero.
Philip Kirkbride,

Risposte:


35
grep -vx 0

Da man grep:

-x, --line-regexp
       Select only those matches that exactly match the whole line.
       For a regular expression pattern, this is like parenthesizing
       the pattern and then surrounding it with ^ and $.

-wfallisce perché il primo 0in 0.02è considerato una "parola", e quindi questa linea è abbinata. Questo perché è seguito da un carattere "non-parola". Puoi vederlo se esegui il comando originale senza -v, ad es grep -w "0".


Potresti anche usare l' -Fopzione dal momento che non stiamo usando schemi regex, solo una semplice corrispondenza di stringhe
glenn jackman

@glennjackman Forse l'ho letto prima, ma non riesco a trovarlo ora. Correre con -F(sorprendentemente per me) sembra richiedere un tempo simile o anche leggermente più lento (~ 5-10%). Quindi, non sono sicuro di quale sarebbe il vantaggio.
Sparhawk,

2
È possibile che il motore RegEx sia usato così spesso e così ampiamente che hanno implementato una versione molto efficiente di esso, ma che una "ricerca semplice" probabilmente non è stata aggiornata per 30 anni.
Nelson,

@Sparhawk: greppresumibilmente ha un caso speciale per regex senza metacaratteri, perché è un caso d'uso comune. È sorprendente che fgrepsarebbe più lento, ma non sorprende che l'overhead di notare questo caso speciale durante la compilazione di un modello breve sia trascurabile rispetto al tempo di scansionare un file di grandi dimensioni. (Se è necessario un caso speciale per andare così in fretta, contro uno schema con una classe di personaggi o x.*y.)
Peter Cordes,

Ma questa è forse una semplificazione eccessiva perché l'input è in realtà molte linee brevi (non una stringa gigante). Dimentico se grepriconosce qualsiasi carattere diverso da \nnewline come separatore di riga. In caso contrario, l'implicito ^e $ può ancora trasformarsi in una ricerca a stringa fissa come strstr(big_buf, "\n0\n"). (O 0\nall'inizio di un buffer.) Ma non stiamo solo cercando la prima corrispondenza potenzialmente lontana in un buffer di grandi dimensioni, ma vogliamo filtrare in modo efficiente. Comunque, in teoria sì, è solo un memcmp a 2 byte all'inizio di ogni riga, e speri che sia fgrep che grep lo vedano.
Peter Cordes,

28

Con grep:

grep -v "^0$" file

^significa l'inizio della linea, $significa fine della linea.


2
Questo è ciò che l'utente ha chiesto: evitare qualsiasi riga contenente solo 1 "0".
Olivier Dulac,

1
Non metterei un segno letterale di dollaro tra virgolette doppie come quella.
user541686,

@mehrdad non è un grosso problema con regex dato che di solito è l'ultimo char o il prossimo non sarà[a-Z0-9]
Sampo Sarrala - codidact.org

14

Sebbene grep possa essere utilizzato per questo (come mostrano chiaramente altre risposte), facciamo un passo indietro e pensiamo a ciò che vuoi effettivamente:

  • Hai un file contenente numeri
  • Si desidera eseguire il filtro in base al valore numerico .

Regex interpreta i dati della sequenza di caratteri. Non conoscono i numeri, ma solo le singole cifre (e le loro combinazioni regolari). Sebbene nel tuo caso particolare ci sia un semplice trucco attorno a questa limitazione, alla fine è una mancata corrispondenza dei requisiti.

A meno che non ci sia un ottimo motivo per usarlo grepqui (ad esempio perché l'hai misurato, ed è notevolmente più efficiente, e l'efficienza è cruciale nel tuo caso), ti consiglio di utilizzare uno strumento diverso.

awk, ad esempio, può filtrare in base a confronti numerici, ad esempio:

awk '$1 == 0' your_file

Ma anche, per ottenere tutte le righe contenenti numeri maggiori di zero:

awk '$1 > 0' your_file

Adoro regex, è un ottimo strumento. Ma non è l' unico strumento. Come dice il proverbio, se tutto ciò che hai è grep, tutto sembra un linguaggio normale.


3
Sono pienamente d'accordo sul fatto che awk possa essere più elegante qui ... tuttavia, corrisponderà forse un po 'più di quello che l'utente si aspetta (ogni valore numerico che valuta 0). Vale a dire, printf '0\n1\n-1\na\nb\n0\n0 also\n0.0\n-0.0\n0*0\n' | awk '($1 == 0)'corrisponderà: 0, 0.0e -0.0... e anche 0 also! Non solo "0". (che a volte è necessario, a volte no). Se l'utente desidera solo "0": awk '/^0$/' (o grep '^0$'). Inoltre dovresti modificare: l'utente deve aggiungere !per annullare il test, quindi nasconde 0(e altri zero) e visualizza il resto. cioè:awk '!( $0 == 0)'
Olivier Dulac,

1
@Olivier, oppure controlla il valore della stringa:$1 == "0"
glenn jackman,

1
@OlivierDulac Ho usato esplicitamente >piuttosto che !=(o, equivalentemente, ! (… == …)) per evidenziare che si tratta di un confronto numerico arbitrario, non solo dell'uguaglianza. Per quanto riguarda l'altro tuo commento, questo è del tutto vero, ma in sostanza siamo tornati nel territorio di confronto delle stringhe e la soluzione esistente utilizza le grepopere (anche se awkovviamente funziona anche).
Konrad Rudolph,

@KonradRudolph fair points :)
Olivier Dulac,

1
@glennjackman: bel trucco davvero. Ma poi OP preferirebbe fare dei test$0=="0"
Olivier Dulac,

5

grep's -wè un po' contorto in modo da dividere la stringa originale in componenti di parole e non parole (qualsiasi cosa tranne lettere, cifre o trattino basso). Dal momento che ha già incontrato una parola valida costituente 0in 0.02essa aveva affermato la logica di negazione per rimuovere la linea.

L'uso sedè un po 'facile in questo contesto per rimuovere solo le parole intere corrispondenti

sed '/^0$/d' file

3

Quando le righe che desideri eliminare contengono solo una 0 seguita dalla riga successiva, puoi selezionare quelle righe emettendo il seguente comando:

grep -v "^0$"

Questo stamperà solo le occorrenze 0che si trovano alla fine di una riga e all'inizio di una riga contemporaneamente. L' -vopzione inverte quindi la nostra selezione.


1
Questa risposta è quasi identica a quella di Arkadiusz Drabczyk, ma te ne sei dimenticata -v, quindi non funziona.
Sparhawk,

Hai ragione. Stavo scrivendo mentre ha pubblicato la sua risposta, quindi non ho visto che è già stata data. Ho letto male quella parte con l' -vopzione, grazie!
majesticLSD

0
  • \ b - bordo parola

grep -v "\b0\b"

  • abbina l'inizio della linea, il modello e la fine della linea

grep -v "^0$"

  • o come suggerito da @Sparhawk -vx lineregexp

-w funziona, ma nel tuo caso 0.2 sono due parole perché il carattere punto è un separatore di parole.


grep -v "\b0\b"non funziona davvero qui. Quale versione di grep usi?
Arkadiusz Drabczyk,

funziona con grep (BSD grep) 2.5.1-FreeBSDsu macOS e grep (GNU grep) 2.16su Ubuntu
Jakub Jindra,

1
Uso di regex GNU \<e \>come limiti di parole, ma ciò avrà lo stesso effetto di-w
glenn jackman

0

Un'altra risposta per motivi di varietà, supponendo che tu abbia un PCRE abilitato grep

grep -Pv "^0(?!\.)"

questo esegue un lookahead negativo per abbinare le linee che iniziano con 0e non sono seguite da un punto. Quindi -velimina le righe non corrispondenti. Puoi vedere in azione qui


1
Questo rimuoverà anche linee come 0123, che non è ciò che l'OP vuole
iruvar

0

Supponendo che una riga che non sia solo un singolo 0 abbia un punto

grep '\.' file

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.