comando grep per visualizzare tutte le righe che iniziano e finiscono con lo stesso carattere


8

Voglio sapere come utilizzare grepper visualizzare tutte le righe che iniziano e finiscono con lo stesso carattere.

Risposte:


14

POSIXly:

pattern='\(.\).*\1
.'
grep -x -- "$pattern" file

Non funzionerà se la riga inizia o termina con un carattere byte non valido, se si desidera coprire quel caso, è possibile aggiungere LC_ALL=C, anche se LC_ALL=Cfunziona solo con i dati carattere a byte singolo.


perl6 sembra essere lo strumento migliore, se lo hai nella tua scatola:

$ printf '\ue7\u301 blah \u107\u327\n121\n1\n123\n' |
  perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'
ḉ blah ḉ
121
1

Anche se soffoca ancora su caratteri non validi.


Nota che perl6modificherà il testo trasformandolo nella NFCforma:

$ printf '\u0044\u0323\u0307\n' |
  perl6 -pe ''                  |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+1e0c
U+0307
U+000a

$ printf '\u0044\u0323\u0307\n' |
  perl -pe ''                   |
  perl -CI -ne 'printf "U+%04x\n", ord for split //'
U+0044
U+0323
U+0307
U+000a

Internamente, perl6memorizza la stringa nella NFGforma (acronimo di Normalization Form Grapheme), che è il perl6modo inventato per gestire correttamente i grafemi non precomposti:

$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say'
1
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say'
2

2
La gestione del testo Unicode da parte di Perl è a dir poco esemplare, al punto che molte attività "semplici" in Perl sono praticamente impossibili da implementare utilizzando altri strumenti, almeno con lo stesso livello di correttezza.
Dietrich Epp,

1
Va notato che perl6modificherà il testo (girarlo in NFC (modulo di normalizzazione "composto")).
Stéphane Chazelas,

@ StéphaneChazelas: Sì, punto giusto. Si noti inoltre che string in perl6è store in NFGform ( Gfor Grapheme), che è un perl6modo per gestire correttamente i grafemi non precomposti.
cuonglm,

10

Non grep ma awk:

awk -F "" 'NF && $1 == $NF'

Questi casi speciali vengono gestiti:

  • non stampa righe vuote
  • stampa sempre righe di 1 carattere

Un FS vuoto divide la scheda in un carattere per ogni campo gawk, mawke busybox awk(byte, non caratteri per gli ultimi due), ma non è standard e non funziona nelle implementazioni della awkderivata da quella originale A, W e K come su BSD e Unice commerciali. Più portatile ma più da digitare:

awk '/./ && substr($0,1,1) == substr($0,length)'

1
Si noti che FScome stringa vuota non è standard e non funzionerà in alcune awkimplementazioni.
cuonglm,

2
Alternativa che evita la scissione ed è completamente portatile (anche per il terribile 'vecchio' awk di Solaris) awk 'length&&substr($0,1,1)==substr($0,length)'(nota l'argomento predefinito di lengthis $0, e l'azione di default è {print $0})
dave_thompson_085

@ dave_thompson_085: grazie, sto solo usando il tuo suggerimento di azione predefinito per avere il comando più breve.
rudimeier,

Firne. Una correzione minore; il mio test per il vecchio awk di Solaris è stato errato (avevo accidentalmente attivato xpg4) ma questo metodo funziona in modo nawkquasi negativo :-)
dave_thompson_085,

8
grep -xe '\(.\).*\1' -e .

Esempio:

$ printf '%s\n' il y était cet été  | grep -xe '\(.\).*\1' -e .
y
été

-xè per la corrispondenza esatta (corrispondenza su tutta la riga). \1essendo un riferimento indietro al personaggio catturato in \(.\). Aggiungiamo a -e .per occuparci del caso speciale di una riga contenente un singolo carattere.

Presuppone che l'input contenga un testo valido nella locale corrente.

La corrispondenza è sul carattere , non sul byte (quelli é in UTF-8 sono i due byte 0xc3 0xa9 per esempio), né graphem cluster (non funzionerebbe se quegli é fossero scritti nella loro forma scomposta con eseguito da U + 0301 combinando accento acuto per esempio).

Per lavorare su graphem cluster, con un grepsupporto -Pper PCRE:

$ printf 'e\u0301te\u0301\n' | grep -xPe '(\X).*\1|\X'
été

Ciò presuppone che la decomposizione sia la stessa per i due cluster, ad esempio un espresso in quanto c U+0301 U+0327non corrisponderebbe a uno espresso come c U+0327 U+0301o ć( U+0107) U+0327o ç( U+00E7) U+0301o ḉ ( U+1E09). Per questo, dovresti fare il controllo su un modulo normalizzato:

$ printf '\ue7\u301 blah \u107\u327\n' |
  perl -MUnicode::Normalize -C -ne '
    print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/'
ḉ blah ḉ

1
Se hai perl6, allora perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'dovrebbe fare tutto il lavoro per te.
cuonglm,

1

Alternativa rapida a python2:

python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt

Esempio:

$ python -c 'import sys;[sys.stdout.write(l) for l in sys.stdin if len(l)>1 and l.rstrip("\n").endswith(l[0])]' < input.txt  | cat -A 
nathan$
 ookie $
a line a$

Non ha esito positivo se la riga contiene spazi finali o iniziali, ad esempio "121".
cuonglm,

@cuonglm è vero. Ma il trailing o il leader degli spazi bianchi era un requisito? Questo fa il lavoro richiesto - controlla se il carattere iniziale e l'ultimo sono uguali. Lo spazio bianco è ancora un personaggio ASCII, no?
Sergiy Kolodyazhnyy,

A proposito di @cuonglm, il tuo ha fallito anche nel trascinamento e nello spazio di testa :)
Sergiy Kolodyazhnyy,

Il codice rimuove gli spazi bianchi iniziali e finali, quindi modifica la riga di input. Inoltre fornisce un errore per le righe vuote.
rudimeier,

@Serg: come? la mia risposta è solo grepping, non modifica l'input.
cuonglm,
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.