comando grep para mostrar todas las líneas que comienzan y terminan con el mismo carácter

8

Quiero saber cómo usar greppara mostrar todas las líneas que comienzan y terminan con el mismo carácter.

Nayan Jariwala
fuente

Respuestas:

14

POSIXY:

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

No funcionará si la línea comienza o termina con un carácter de byte no válido, si desea cubrir ese caso, puede agregar LC_ALL=C, aunque LC_ALL=Csolo funciona con datos de caracteres de un solo byte.


perl6 parece ser la mejor herramienta, si la tiene en su caja:

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

Aunque todavía se ahoga con caracteres no válidos.


Tenga en cuenta que perl6alterará su texto convirtiéndolo en 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, perl6almacena la cadena en NFGforma (soporte Normalization Form Grapheme), que es una perl6forma inventada de tratar correctamente los grafemas no precompuestos:

$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.chars.say'
1
$ printf '\u0044\u0323\u0307\n' | perl6 -ne '.codes.say'
2
Cuonglm
fuente
2
El manejo de Perl del texto Unicode es nada menos que ejemplar, hasta el punto de que muchas tareas "simples" en Perl son prácticamente imposibles de implementar utilizando otras herramientas, al menos con el mismo nivel de corrección.
Dietrich Epp
1
Sin perl6embargo, debe tenerse en cuenta que alterará el texto (gírelo a NFC (formulario de normalización "compuesto")).
Stéphane Chazelas
@ StéphaneChazelas: Sí, punto justo. También tenga en cuenta que string in perl6se almacena en NFGforma ( Gfor Grapheme), que es una perl6forma de tratar correctamente los grafemas no precompuestos.
Cuonglm
10

No grep pero awk:

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

Estos casos especiales se manejan:

  • no imprime líneas vacías
  • siempre imprime líneas de 1 carácter

Un FS vacío divide el registro en un solo personaje por campo en gawk, mawky busybox awk(bytes, no caracteres de los dos últimos), pero no es estándar y no funciona en las implementaciones de la awkderiva de la original por A, W y K como en BSD y unidades comerciales. Más portátil pero más para escribir:

awk '/./ && substr($0,1,1) == substr($0,length)'
rudimeier
fuente
1
Tenga en cuenta que FScomo cadena vacía no es estándar, y no funcionará en alguna awkimplementación.
Cuonglm
2
Alternativa que evita la división y es totalmente portátil (incluso para el 'viejo' awk de Solaris) awk 'length&&substr($0,1,1)==substr($0,length)'(observe el argumento predeterminado de lengthis $0, y la acción predeterminada es {print $0})
dave_thompson_085
@ dave_thompson_085: gracias, solo estoy usando tu sugerencia de acción predeterminada para tener el comando más corto.
rudimeier
Firne Una corrección menor; mi prueba para el viejo awk de Solaris fue errónea (accidentalmente tenía xpg4 encendido) pero este método funciona, lo nawkcual es casi tan malo :-)
dave_thompson_085
8
grep -xe '\(.\).*\1' -e .

Ejemplo:

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

-xes para la coincidencia exacta (coincidencia en toda la línea). \1siendo una referencia al personaje capturado en \(.\). Agregamos un -e .para cuidar el caso especial de una línea que contiene un solo carácter.

Se supone que la entrada contiene texto válido en la configuración regional actual.

La coincidencia está en el carácter , no en el byte (esos é en UTF-8 son los dos bytes 0xc3 0xa9 por ejemplo), ni en el grupo de gráficos (no funcionaría si esos é se escribieran en su forma descompuesta cone seguido por U + 0301 combinando acento agudo, por ejemplo).

Para trabajar en clústeres de gráficos, con un grepsoporte -Ppara PCRE:

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

Eso supone que la descomposición es la misma para los dos grupos, por ejemplo, un expresado como c U+0301 U+0327no coincidiría con uno expresado como c U+0327 U+0301o ć( U+0107) U+0327o ç( U+00E7) U+0301o ḉ ( U+1E09). Para eso, deberías hacer la verificación en un formulario normalizado:

$ printf '\ue7\u301 blah \u107\u327\n' |
  perl -MUnicode::Normalize -C -ne '
    print if /^\X$/ || NFC($_) =~ /^(\X).*\1$/'
ḉ blah ḉ
Stéphane Chazelas
fuente
1
Si es así perl6, perl6 -ne '.say if m/^(.).*$0$/ || /^.$/'debería hacer todos los trabajos por usted.
Cuonglm
1

Alternativa rápida 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

Ejemplo:

$ 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$
Sergiy Kolodyazhnyy
fuente
Se produce un error si la línea contiene espacios finales o iniciales, ejemplo `121`.
Cuonglm
@cuonglm eso es cierto. ¿Pero era un requisito el espacio en blanco final o posterior? Esto hace el trabajo solicitado: compruebe si el carácter inicial y el último son los mismos. El espacio en blanco sigue siendo un carácter ascii, ¿no?
Sergiy Kolodyazhnyy
@cuonglm el tuyo también falló con el espacio al final y al final, por cierto :)
Sergiy Kolodyazhnyy
Su código elimina los espacios en blanco iniciales y finales, por lo que cambia la línea de entrada. También da un error para líneas vacías.
rudimeier
@Serg: ¿Cómo? mi respuesta solo grepping, no modifica la entrada.
Cuonglm