Esistono due modi per interpretare questa domanda; Tratterò entrambi i casi. Potresti voler visualizzare le righe:
- che contengono una sequenza di quattro cifre che non fa parte di una sequenza più lunga di cifre, oppure
- che contiene una sequenza di quattro cifre ma non più una sequenza di cifre (nemmeno separatamente).
Ad esempio, verrà visualizzato (1) 1234a56789
, ma (2) non verrà visualizzato.
Se si desidera visualizzare tutte le righe che contengono una sequenza di quattro cifre che non fa parte di una sequenza più lunga di cifre, un modo è:
grep -P '(?<!\d)\d{4}(?!\d)' file
Questo usa espressioni regolari Perl , che Ubuntu grep
( GNU grep ) supporta tramite -P
. Non corrisponderà a testo simile 12345
, né corrisponderà al 1234
o 2345
che ne fanno parte. Ma corrisponderà al 1234
di 1234a56789
.
Nelle espressioni regolari Perl:
\d
indica qualsiasi cifra (è un modo breve per dire [0-9]
o [[:digit:]]
).
x{4}
partite x
4 volte. (La {
}
sintassi non è specifica per le espressioni regolari Perl; è presente anche in espressioni regolari estese grep -E
). \d{4}
Lo stesso vale per \d\d\d\d
.
(?<!\d)
è un'asserzione look-behind negativa di larghezza zero. Significa "se non preceduto da \d
."
(?!\d)
è un'asserzione di previsione negativa di larghezza zero. Significa "a meno che non sia seguito da \d
".
(?<!\d)
e (?!\d)
non abbinare il testo al di fuori della sequenza di quattro cifre; invece (se usati insieme) impediranno di far corrispondere una sequenza di quattro cifre se fa parte di una sequenza più lunga di cifre.
Usare solo il look-behind o solo il look-ahead non è sufficiente perché la sottosequenza di quattro cifre più a destra o a sinistra sarebbe ancora abbinata.
Uno dei vantaggi dell'utilizzo delle asserzioni look-behind e look-ahead è che il modello corrisponde solo alle sequenze di quattro cifre stesse e non al testo circostante. Questo è utile quando si utilizza l'evidenziazione del colore (con l' --color
opzione).
ek@Io:~$ grep -P '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
12345abc789d0123e4
Di default in Ubuntu, ogni utente ha alias grep='grep --color=auto'
nel proprio ~.bashrc
file . Quindi si ottiene automaticamente l'evidenziazione del colore quando si esegue un semplice comando che inizia con grep
(questo è quando gli alias vengono espansi) e l'output standard è un terminale (questo è ciò che controlla). Le partite sono in genere evidenziate in una sfumatura di rosso (vicino al vermiglio ), ma l'ho mostrato in grassetto corsivo. Ecco uno screenshot:--color=auto
E puoi persino grep
stampare solo il testo corrispondente, e non l'intera riga, con -o
:
ek@Io:~$ grep -oP '(?<!\d)\d{4}(?!\d)' <<< 12345abc789d0123e4
0123
Modo alternativo, senza asserzioni Look-Behind e Look-Ahead
Tuttavia, se tu:
- è necessario un comando che verrà eseguito anche su sistemi in cui
grep
non supporta -P
o non desidera utilizzare un'espressione regolare Perl e
- non è necessario abbinare in modo specifico le quattro cifre, come di solito accade se il tuo obiettivo è semplicemente quello di visualizzare linee contenenti corrispondenze e
- va bene con una soluzione un po 'meno elegante
... allora puoi ottenerlo con un'espressione regolare estesa :
grep -E '(^|[^0-9])[0-9]{4}($|[^0-9])' file
Questo corrisponde a quattro cifre e al carattere non cifra - o all'inizio o alla fine della linea - che le circonda. In particolare:
[0-9]
corrisponde a qualsiasi cifra (come [[:digit:]]
, o \d
nelle espressioni regolari Perl) e {4}
significa "quattro volte". Quindi [0-9]{4}
corrisponde a una sequenza di quattro cifre.
[^0-9]
partite caratteri non nella gamma di 0
tramite 9
. È equivalente a [^[:digit:]]
(o \D
, nelle espressioni regolari Perl).
^
, quando non appare tra [
]
parentesi, corrisponde all'inizio di una riga. Allo stesso modo, $
corrisponde alla fine di una riga.
|
mezzi o e le parentesi sono per il raggruppamento (come in algebra). Quindi (^|[^0-9])
corrisponde all'inizio della riga o a un carattere non cifra, mentre ($|[^0-9])
corrisponde alla fine della riga o a un carattere non cifra.
Quindi le corrispondenze si verificano solo nelle righe che contengono una sequenza di quattro cifre ( [0-9]{4}
) che è contemporaneamente:
- all'inizio della riga o preceduto da una non cifra (
(^|[^0-9])
) e
- alla fine della riga o seguito da una non cifra (
($|[^0-9])
).
Se, d'altra parte, vuoi visualizzare tutte le righe che contengono una sequenza di quattro cifre, ma non contengono alcuna sequenza di più di quattro cifre (anche una che è separata da un'altra sequenza di sole quattro cifre), concettualmente il tuo l'obiettivo è trovare linee che corrispondano a un modello ma non a un altro.
Pertanto, anche se sai come farlo con un singolo motivo, suggerirei di usare qualcosa come il secondo suggerimento di grep
Matt , ing per i due motivi separatamente.
Non trarrai alcun vantaggio da nessuna delle funzionalità avanzate delle espressioni regolari Perl quando lo fai, quindi potresti preferire non usarle. Ma in linea con lo stile sopra, ecco un accorciamento della soluzione di matt usando \d
(e parentesi graffe) al posto di [0-9]
:
grep -P '\d{4}' file | grep -Pv '\d{5}'
Dal momento che utilizza [0-9]
, il modo di matt è più portatile - funzionerà su sistemi in cui grep
non supporta le espressioni regolari Perl. Se usi [0-9]
(o [[:digit:]]
) invece di \d
, ma continui a usarlo {
}
, otterrai la portabilità di Matt in modo un po 'più conciso:
grep -E '[0-9]{4}' file | grep -Ev '[0-9]{5}'
Modo alternativo, con un unico motivo
Se davvero preferisci un grep
comando che
- usa una singola espressione regolare (non due
grep
s separate da una pipe , come sopra)
- per visualizzare le righe che contengono almeno una sequenza di quattro cifre,
- ma nessuna sequenza di cinque (o più) cifre,
- e non ti dispiace abbinare l'intera linea, non solo le cifre (probabilmente non ti dispiace)
... allora puoi usare:
grep -Px '(\d{0,4}\D)*\d{4}(\D\d{0,4})*' file
Il -x
flag fa grep
visualizzare solo le linee in cui l'intera riga corrisponde (anziché qualsiasi riga contenente una corrispondenza).
Ho usato un'espressione regolare Perl perché penso che la brevità \d
e \D
aumentare sostanzialmente la chiarezza in questo caso. Ma se hai bisogno di qualcosa di portatile per i sistemi grep
che non supportano -P
, puoi sostituirli con [0-9]
e [^0-9]
(o con [[:digit:]]
e [^[:digit]]
):
grep -Ex '([0-9]{0,4}[^0-9])*[0-9]{4}([^0-9][0-9]{0,4})*' file
Il modo in cui funzionano queste espressioni regolari è:
Nel mezzo \d{4}
o [0-9]{4}
corrisponde a una sequenza di quattro cifre. Potremmo avere più di uno di questi, ma dobbiamo averne almeno uno.
A sinistra (\d{0,4}\D)*
o ([0-9]{0,4}[^0-9])*
corrisponde a zero o più ( *
) istanze di non più di quattro cifre seguite da una non cifra. Le cifre zero (ovvero nulla) sono una possibilità per "non più di quattro cifre". Questo corrisponde a (a) la stringa vuota o (b) qualsiasi stringa che termina in una non cifra e che non contiene sequenze di più di quattro cifre.
Poiché il testo immediatamente a sinistra della centrale \d{4}
(o [0-9]{4}
) deve essere vuoto o terminare con una non cifra, ciò impedisce alla centrale \d{4}
di far corrispondere quattro cifre che hanno un'altra (quinta) cifra a sinistra di esse.
A destra, (\D\d{0,4})*
o ([^0-9][0-9]{0,4})*
corrisponde a zero o più ( *
) istanze di una non cifra seguita da non più di quattro cifre (che, come prima, potrebbero essere quattro, tre, due, una o addirittura nessuna). Questo corrisponde a (a) la stringa vuota o (b) qualsiasi stringa che inizia in una non cifra e non contiene sequenze di più di quattro cifre.
Poiché il testo immediatamente a destra della centrale \d{4}
(o [0-9]{4}
) deve essere vuoto o iniziare con una non cifra, questo impedisce alla centrale \d{4}
di far corrispondere quattro cifre che hanno un'altra (quinta) cifra appena alla loro destra.
Ciò garantisce che una sequenza di quattro cifre sia presente da qualche parte e che nessuna sequenza di cinque o più cifre sia presente ovunque.
Non è male o sbagliato farlo in questo modo. Ma forse il motivo più importante per considerare questa alternativa è che chiarisce invece il vantaggio dell'uso (o simile), come suggerito sopra e nella risposta di Matt .grep -P '\d{4}' file | grep -Pv '\d{5}'
In questo modo, è chiaro che il tuo obiettivo è selezionare linee che contengono una cosa ma non un'altra. Inoltre la sintassi è più semplice (quindi può essere compresa più rapidamente da molti lettori / manutentori).
1234a12345
essere visualizzata o no?