¿Por qué el anclaje de fin de línea $ no funciona con el comando grep, a pesar de que el ancla ^ de frente de línea sí lo está?

19

Muy nuevo en UNIX pero no nuevo en programación. En la terminal de MacBook. A los efectos de la gestión y búsqueda de listas de palabras para la construcción de crucigramas, estoy tratando de conseguir práctico con el comando grep y sus variaciones. Parece bastante sencillo, pero cuando se quedaba colgado desde el principio con lo que pensé que debería ser un simple caso.

Cuando entro

grep "^COW" masternospaces.txt

Obtengo lo que quiero: una lista de todas las palabras que comienzan con COW.

Pero cuando entro

grep "COW$" masternospaces.txt

Espero obtener una lista de palabras que terminan en VACA (hay muchas de esas palabras), y no se devuelve nada.

El archivo es un archivo de texto sin formato, con cada línea solo una palabra (o una frase de palabra sin espacios) en mayúsculas.

¿Alguna idea de lo que podría estar pasando aquí?

DTalvacchio
fuente
3
¿Cuál es el origen del archivo masternospaces.txt? ¿Es posible que tenga terminaciones de línea de estilo Windows (CR-LF) en lugar de LF de estilo Unix?
steeldriver
2
No estoy seguro, pero ¿estás buscando una lista de palabras o una lista de líneas ... ?
mikeserv
Steeldriver-- Algo así fue mi primer pensamiento. No estaba seguro de cómo inspeccionar lo que estaba sucediendo allí, ni cuáles eran las posibilidades. Se supone que un retorno final era un retorno final. Ese archivo es un compendio masivo de algunas fuentes. Ni siquiera estoy seguro de cuál se consideraría el archivo original. Y ha pasado por al menos tres procesadores de texto en máquinas PC y Mac. ¿Cuál podría ser la mejor manera de ver qué tipo de terminaciones está usando?
DTalvacchio
mikeserv-- En este archivo .txt, cada línea es solo una palabra (o una frase sin espacios entre palabras, así que de nuevo una "palabra"). Así que estoy buscando líneas, supongo. . . solo que cada línea tiene solo una de las que considero una palabra para crucigramas.
DTalvacchio
1
Puede usar hexdumppara verificar exactamente cómo están formateadas las terminaciones de línea. Le sugiero que use mi formato favorito: hexdump -e '"%08_ad (0x%08_ax) "8/1 "%02x "" "8/1 "%02x "' -e '" "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt. Con la salida, verifique los finales de línea: 0a-> LF, 0d-> CR.
user43791

Respuestas:

23

Como mencionó @steeldriver, es probable que el problema sea causado por un estilo de final de línea diferente al grepesperado.

Para comprobar los finales de línea

Puede usar hexdumppara verificar exactamente cómo están formateadas las terminaciones de línea. Le sugiero que use mi formato favorito:

hexdump -e '"%08_ad (0x%08_ax)    "8/1 "%02x ""   "8/1 "%02x "' -e '"    "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt

Con la salida, verifique los finales de línea: 0a-> LF, 0d-> CR. Un ejemplo muy rápida daría algo como esto:

$ hexdump -e '"%08_ad (0x%08_ax)    "8/1 "%02x ""   "8/1 "%02x "' -e '"    "8/1 "%_p""|"8/1 "%_p""\n"' masternospaces.txt
00000000 (0x00000000)    4e 6f 20 43 4f 57 20 65   6e 64 69 6e 67 0d 0a 45    No COW e|nding..E
00000016 (0x00000010)    6e 64 69 6e 67 20 69 6e   20 43 4f 57 0d 0a          nding in| COW..

Tenga en cuenta los finales de línea en formato DOS: 0d 0a.

Para cambiar los finales de línea

Se puede ver aquí o aquí para diversos métodos para cambiar los finales de línea utilizando diversas herramientas, pero para una cosa de una sola vez, usted podría utilizar siempre vi / vim:

vim masternospaces.txt
:set fileformat=unix
:wq

Grep sin cambiar nada

Si solo desea grepcoincidir sin importar el final de línea, siempre puede especificar terminaciones de línea como esta:

grep 'COW[[:cntrl:]]*$' masternospaces.txt

Si se muestra una línea en blanco, puede verificar que realmente coincide con algo mediante la -vopción de cat:

grep 'COW[[:cntrl:]]*$' masternospaces.txt | cat -v

Mi favorito personal

También puede grep y estandarizar la salida usando sed:

sed -n '/COW^M*$/{;s/^M//g;p;};' masternospaces.txt

donde ^Mse obtiene escribiendo Ctrl-V Ctrl-Men su teclado.

¡Espero que esto ayude!

user43791
fuente
Eso es todo extremadamente útil. Estoy fuera de tiempo, pero hoy en día se verá a través de todo esto de cerca mañana y ver qué es lo que. Si mientras tanto alguno de ustedes tiene un enlace a su guía de referencia de comandos de Unix favorita para que pueda enseñarme un poco sobre cómo funcionan las cosas, lo agradecería. He estado recogiendo piezas aquí y allá, pero aún no he encontrado una fuente que sea mi explicación. Gracias a todos y nos registraremos mañana con una actualización que esperamos sea exitosa. -D
DTalvacchio
Es una pena que esta publicación no tenga cierre, al menos para mí. No puedo, por mi vida, descubrir cómo hacer coincidir el final de la línea. Si hago un volcado hexadecimal, no puedo encontrar una buena línea que termine como en el ejemplo anterior. No estoy familiarizado con el trabajo con hexadecimal, por lo que es posible que no lo esté leyendo correctamente. También probé el [[:cntrl:]]@ user43791 sugerido y todavía no coincide con nada para mí. Esto no tiene sentido. Estoy usando GNU grep 2,20 y Análisis de la salida de NDpi que se escribe en un archivo de texto
Harperville
@harperville Si tú cat -v yourfile.ext, ¿qué ves?
user43791
Bueno, nada emocionante o inesperado. Solo los contenidos como esperaría verlos. ¿Algo específico que estás buscando? No puedo pegar la salida aquí, pero solo veo el contenido. "Texto en inglés ASCII" antiguo de acuerdo con file.
Harperville
@harperville ¿No hay "^ M" extra al final de cada línea? ¿Podría pegar las primeras líneas de hexadecimal?
user43791
1

Aunque puede usar la sintaxis RegEx 'estándar' con grep (como en la respuesta de @ user43791 ), grep también tiene otros identificadores para indicar los límites de entrada.

Los marcadores para el inicio y el final de toda la línea son \`(retroceso) (en lugar de ^) y \'(apóstrofe) (en lugar de $).

Entonces, para su comando original, usaría: grep "COW\'" masternospaces.txt

Nota al margen: también es importante tener en cuenta eso ?y +se tratará literalmente a menos que escapes de ellos \?y los \+conviertas en sus contrapartes selectoras de estilo RegEx.

Fuente: grepsintaxis de expresiones regulares

samthecodingman
fuente
grep está tomando ^ (caret) para comenzar y \ '(apóstrofe) para terminar
GypsyCosmonaut
1

Otra forma de eliminar el \rantes del grep:

... | dos2unix | egrep 'COW$' | ...

Me gusta que esté muy claro ya que no recuerdo cosas como [[:cntrl:]]por mucho tiempo.

Javier
fuente
-2

"COW $" cuando bash estableció el parámetro para grep, se interpretó como 'COW' donde se trata "$" como "", porque $ es un símbolo de escape. cuando $ no se comparó con nada, bash shell lo interpreta como una cadena vacía, por lo que debe usar grep 'COW $' masternospaces.txt en su lugar.

yangyang
fuente
3
ya que no hay expansión del válida $, que se quedaría solo por bash y usada por grep. Compruébelo usted mismo: echo "COW$"la $voluntad seguirá allí.
Jeff Schaller
-3

En BSD grep tiene que escapar "$" y encerrar la cadena entre comillas dobles:

"COW\$"
usuario297403
fuente
1
Mmm no. El $no será especial para el shell, debido a que el material después de que no es un nombre de variable de shell válida. El uso de comillas simples cadenas estáticas es una mejor idea, pero no hará ninguna diferencia en este caso.
Kusalananda