Trovare testo tra due caratteri o stringhe specifici


17

Dire che ho linee come questa:

*[234]*
*[23]*
*[1453]*

dove *rappresenta qualsiasi stringa (tranne una stringa del modulo [number]). Come posso analizzare queste righe con un'utilità della riga di comando ed estrarre il numero tra parentesi?

Più in generale, che di questi strumenti cut, sed, grepo awksarebbe opportuno per tale compito?

Risposte:


16

Se hai GNU grep, puoi usare la sua -oopzione per cercare un regex e produrre solo la parte corrispondente. (Altre implementazioni grep possono mostrare solo l'intera riga.) Se ci sono più corrispondenze su una riga, vengono stampate su righe separate.

grep -o '\[[0-9]*\]'

Se vuoi solo le cifre e non le parentesi, è un po 'più difficile; devi usare un'asserzione di larghezza zero: una regexp che corrisponde alla stringa vuota, ma solo se è preceduta o seguita, a seconda dei casi, da una parentesi. Le asserzioni di larghezza zero sono disponibili solo nella sintassi Perl.

grep -P -o '(?<=\[)[0-9]*(?=\])'

Con sed, è necessario disattivare la stampa -ne abbinare l'intera linea e conservare solo la parte corrispondente. Se sono presenti più corrispondenze possibili su una riga, viene stampata solo l'ultima corrispondenza. Vedi Estrarre un regex abbinato a 'sed' senza stampare i personaggi circostanti per maggiori dettagli sull'uso di sed qui.

sed -n 's/^.*\(\[[0-9]*\]\).*/\1/p'

o se vuoi solo le cifre e non le parentesi:

sed -n 's/^.*\[\([0-9]*\)\].*/\1/p'

Senza grep -o, Perl è lo strumento di scelta qui se vuoi qualcosa che sia sia semplice che comprensibile. Su ogni riga ( -n), se la riga contiene una corrispondenza per \[[0-9]*\], quindi stampare quella corrispondenza ( $&) e una nuova riga ( -l).

perl -l -ne '/\[[0-9]*\]/ and print $&'

Se si desidera solo le cifre, inserire le parentesi nella regex per delimitare un gruppo e stampare solo quel gruppo.

perl -l -ne '/\[([0-9]*)\]/ and print $1'

PS Se si desidera richiedere solo una o più cifre tra parentesi, passare [0-9]*a [0-9][0-9]*o a [0-9]+in Perl.


Tutto bene, a parte quello che vuole "estrarre il numero tra parentesi". Penso che "tranne [number]" significhi tranne[0-9]
Peter

1
@ Peter.OI ho capito "tranne [numero]" per indicare che non ci sono altre parti della linea di quel modulo. Ma ho modificato la mia risposta per mostrare come stampare solo le cifre, per ogni evenienza.
Gilles 'SO- smetti di essere malvagio'

1
Quelle perlaffermazioni regex sembrano davvero utili! Ne ho letto dopo averti visto usare affermazioni sia all'indietro che in avanti, anche in grep (avevo disattivato il fatto che puoi scegliere un motore regex). Dedicherò ancora un po 'di tempo alla regex di perl da qui in poi. Grazie ... PS .. Ho appena letto man grep... "Questo è altamente sperimentale e grep -P può avvertire di funzionalità non implementate." ... Spero che ciò non significhi instabile (?) ...
Peter.O

5

Non puoi farlo con cut.

  1. tr -c -d '0123456789\012'
  2. sed 's/[^0-9]*//g'
  3. awk -F'[^0-9]+' '{ print $1$2$3 }'
  4. grep -o -E '[0-9]+'

tr è la soluzione più naturale per il problema e probabilmente funzionerebbe il più veloce, ma penso che avresti bisogno di input giganteschi per separare una di queste opzioni in termini di velocità.


Per sed, ^.*è avido e consuma tutto tranne l'ultima cifra, e +deve essere \+oppure usa il posix \([0-9][0-9]*\).... e comunque 's/[^0-9]*//g'funziona altrettanto bene, ... Thanks for the tr -c` esempio, ma non è forse \012superfluo?
Peter,

@Peter Grazie per averlo colto. Avrei giurato di aver provato l'esempio sed. :( L'ho cambiato nella tua versione. Riguardo \012: è necessario altrimenti trmangeranno le nuove righe.
Kyle Jones

Aha ... Stavo vedendo come \0, 1, 2(o anche \, 0, 1, 2). A quanto pare non sono abbastanza in sintonia con l'ottale. Grazie.
Peter,

4

Se intendi estrarre una serie di cifre consecutive tra caratteri non numerici, immagino sede awksiano i migliori (anche se grepè anche in grado di darti i caratteri corrispondenti):

sed: puoi ovviamente abbinare le cifre, ma è forse interessante fare il contrario, rimuovere le non cifre (funziona fino a quando c'è un solo numero per riga):

$ echo nn3334nn | sed -e 's/[^[[:digit:]]]*//g'
3344

grep: puoi abbinare cifre consecutive

$ echo nn3334nn | grep -o '[[:digit:]]*'
3344

Non faccio un esempio awkperché ho un'esperienza nulla con esso; è interessante notare che, sebbene sedsia un coltellino svizzero, grepti offre un modo più semplice e più leggibile per farlo, che funziona anche per più di un numero su ciascuna riga di input (l' -ounico stampa le parti corrispondenti dell'input, ognuna sulla propria linea):

$ echo dna42dna54dna | grep -o '[[:digit:]]*'
42
54

Proprio come un confronto, ecco un sedeqivalent della "più di un numero per linea" ad esempio grep -o '[[:digit:]]*'. . . sed -nr '/[0-9]/{ s/^[^[0-9]*|[^0-9]*$//g; s/[^0-9]+/\n/g; p}'... (+1)
Peter

2

Dal momento che è stato detto che non è possibile farlo cut, mostrerò che è facilmente possibile produrre una soluzione che non sia almeno peggiore di alcuni degli altri, anche se non approvo l'uso del cut"migliore" (o anche una soluzione particolarmente valida). Va detto che qualsiasi soluzione che non cerca specificamente *[e ]*intorno alle cifre rende le ipotesi semplificanti ed è quindi soggetta a errori su esempi più complessi di quelli forniti da chi chiede (ad esempio cifre all'esterno *[e ]*, che non dovrebbero essere mostrate). Questa soluzione controlla almeno le parentesi e potrebbe essere estesa anche per controllare gli asterischi (lasciato come esercizio al lettore):

cut -f 2 -d '[' myfile.txt | cut -f 1 -d ']'

Questo utilizza l' -dopzione, che specifica un delimitatore. Ovviamente potresti anche inserire l' cutespressione invece di leggere da un file. Sebbene cutsia probabilmente piuttosto veloce, poiché è semplice (nessun motore regex), devi invocarlo almeno due volte (o qualche altro tempo per verificare *), il che crea un sovraccarico di processo. L'unico vero vantaggio di questa soluzione è che è piuttosto leggibile, specialmente per gli utenti occasionali non esperti nei costrutti regex.

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.