hacer salida grep sin arrastrar nueva línea

8

Considere este fragmento:

X=$(grep -m1 'some-pattern' some-file | sed -n 's/.* //p')

Quiero poner la última palabra en una variable si alguna condición de patrón coincide con líneas en un archivo de texto arbitrario

Mi problema es que la variable Xtiene CR o LF o CRLF al final, dependiendo del archivo de origen, del que quiero deshacerme, ya que interfiere con la operación posterior que pretendo hacer.
Incluso probé algo como:

X=$(grep -m1 'some-pattern' some-file | sed -n 's/.* \([A-Za-z]\+\)/\1/p')

por lo tanto, se espera que la sedsalida esté limitada [A-Za-z]+pero aún existen estos molestos bytes dentro de la variable X.

¿Cómo puedo deshacerme de él, sin utilizar demasiada código como ver lo que son bytes al final con xxdentonces cuty similares complicaciones?

zetah
fuente

Respuestas:

4

Parece awkque sería una mejor opción para sus necesidades, ya que estos problemas no existen debido al hecho de que puede usar campos y registros:

x=$(awk '/some-pattern/ { sub(/\r$/, "") ; printf("%s", $NF) ; exit }' some-file)

La sustitución evita su problema con las terminaciones de línea CRLF.

sub(/\r$/, "")elimina el CR final, si existe. Como se awktrata \ncomo el separador de registro (línea), no necesita quitarlo, ya que no está en los datos que se están mirando.

printf("%s", $NF)imprime el campo final ( $NF) sin nueva línea final ( printy algunas otras awkfunciones agregan una nueva línea por defecto).

exitsucede después de las dos primeras acciones, esto es el equivalente de m1en su greplínea de comando. Esto garantiza que awksalga después de la ejecución de los dos comandos anteriores, y dado que estos comandos se emiten en una coincidencia, y awk evalúa los datos de manera FIFO, esto solo imprimirá la primera coincidencia.

Chris Down
fuente
Gracias, se ve elegante pero desafortunadamente CRLF todavía está adentroX
zetah
:) Ahora ya no se ve elegante y todavía no es bueno
zetah
@zetah - No habrá un CR, pero habrá un LF. Me costó mucho entender lo que quieres de la pregunta, espero que mi edición haga lo que quieras.
Chris Down
De acuerdo, esta vez lo hace bien: muestra la última palabra en una línea si esa línea cumple alguna condición de patrón, no sé, tal vez esté claro para mí porque tengo este problema y luego es difícil de explicar como hablante de inglés no nativo . De todos modos, esperaré un poco más si alguien aborda esto con una grep/sedsolución awk(lo cual no entiendo), y si no, lo usaré. Gracias
zetah
@zetah: agregaré una explicación para que puedas entenderla mejor, un segundo.
Chris Down
7

El ``o $()eliminará la nueva línea del final, pero para hacer esto programáticamente, use tr.

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d '\012\015'

Esto eliminará el retorno de carro y / o la nueva línea de la cadena.

Lo que podría ser el problema es cómo se genera el resultado. Por ejemplo, por defecto, echoagrega una nueva línea. Es posible que desee utilizar echo -no printf.

Arcege
fuente
Esto también eliminará los retornos de carro que pueden ocurrir en toda la cadena, lo que puede no ser deseado.
Chris Down
Sí, aunque es posible tener un retorno de carro incrustado dentro de una sola línea, es extremadamente raro. El -m1se asegurará de que sólo hay una salida de línea, que con toda probabilidad, tendría el retorno de carro al final.
Arcege
ah tr... interesante, funciona tanto en archivos LF como CRLF. Pensaría \010\013por alguna razón, y también \f\rfunciona correctamente. Sobre el resultado: en realidad no pongo la salida en variable sino como variable encerrada $()en un patrón para la grepcoincidencia - some pipe | grep -o " $(...) ". Gracias por los comentarios
zetah
3

Prefiero asi

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d '\n'
Steven Penny
fuente
2

Esto funciona para mi:

grep -m1 'some-pattern' some-file | sed -n 's/.* //p' | tr -d "\n" | tr -d "\r"
Funky_Pandy
fuente
0

¿Por qué no simplemente dejar sedhacer la [\r\f]limpieza?

# using Bash's $'string' idiom (that decodes ANSI C escape sequences)
# cf. http://wiki.bash-hackers.org/syntax/quoting#ansi_c_like_strings
- X="$(grep -m1 'some-pattern' some-file | sed -n 's/.* //p')"
+ X="$(grep -m1 'some-pattern' some-file | sed -n -e $'s/[\r\f]*$//' -e 's/.* //p')"

Su segundo enfoque carece de una expresión regular final para coger el CR de salida, \r.

# sample code to remove trailing \r with sed
# cf. http://en.wikipedia.org/wiki/Regular_expression#POSIX_character_classes
printf 'a b c\r' | sed -n 's/^.* \([[:alpha:]]\{1,\}\)/\1/p' | od -c
printf 'a b c\r' | sed -n 's/^.* \([[:alpha:]]\{1,\}\)[[:space:]]*/\1/p' | od -c

# keeps trailing space after c
printf 'a b c \r' | sed -n 's/^.* \([[:alpha:] ]\{1,\}\)[[:space:]]*/\1/p' | od -b
Chad
fuente
0

La versión normal de grep (incluido grep -P) siempre genera un avance de línea con su coincidencia, por lo que si solo tiene un resultado (o solo desea que se elimine el avance de línea agregado final), es suficiente simplemente eliminar el carácter final de la salida, que puede hacer canalizándola head -c-1.

Jon
fuente