¿podemos imprimir la última palabra de cada línea en linux usando el comando sed?

9

supongamos que si hay un archivo que consta de las siguientes líneas, si son

12345 567 7878 66

   er3 t45t y6y46y 


 4y6 y656y y5y

   46y6 65y7 y66uyuy

 aa46y6a

La salida tiene que verse así:

66

y6y46y

y5y

y66uyuyy

y46y6y

He intentado el sed 's/.* //g'nombre de archivo del comando y varios otros sedcomandos, pero no funciona.

¿Puedo saber cuál es el sedcomando exacto ?

Rajeev Nukala
fuente
¿Es obligatorio usarlo sed?
coffeMug

Respuestas:

8
awk '{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//'

Eso todavía imprimiría una línea vacía para cada línea en blanco. Para evitarlo:

awk 'NF{print $NF}'
sed 's/[[:blank:]]*$//;s/.*[[:blank:]]//;/./!d'
Stéphane Chazelas
fuente
Única alternativa expresión: sed -n 's/.*[[:blank:]]\+\([^[:blank:]]\+\)[[:blank:]]*$/\1/p'.
jimmij
@jimmij: ese no funciona si la última secuencia no en blanco es también la primera y no hay espacios en blanco que la precedan. Además, es .*probable que también lo hagas por la cola, probablemente: descartas todo menos los espacios en blanco finales de todos modos w / .*[^[:blank:]].
mikeserv
6

La awkvariable $NFes el último campo de cada registro ; puede usarlo para imprimir solo los últimos campos de su archivo de la siguiente manera:

awk '{print $NF}' file
jasonwryan
fuente
4

Puedes probar :

  • sed 's/.* //'
  • awk '{print $NF}'
Uriel
fuente
4

Ya casi estás ahí. Solo especifique la última palabra:

sed 's/^.* \([^ ][^ ]*\)/\1/g'

Que hace:

  1. '^. *' elimina todo dentro del inicio de la línea y cualquier espacio.
  2. '\ (...) \' coincide con un patrón y lo devuelve como \ 1.
  3. '[^]' coincide con cualquier cosa sin espacio.

(Editado para agregar una mejor solución. ¡Gracias Hildred!)

Tono continuo
fuente
1
Aquí hay una expresión más corta: sed -r 's/.* ([^ ]+)/\1/g'si se permiten expresiones regulares extendidas, que suele ser el caso.
mkalkov
Versión más corta, usando el reemplazo de lo que no desea conservar en lugar de lo que desea conservar:sed 's/.* //'
Uriel
2

Podría usar algún patrón adecuado en greplugar de sed, por ejemplo:

grep -o "[a-Z0-9]*$"

En este ejemplo, [...]contiene rangos de caracteres considerados apropiados para una "palabra" (alfanuméricos en este caso, podrían agregarse otros símbolos, algunos de los cuales deben escaparse).

Dalker
fuente
2
Eso supone que no hay espacios en blanco al final de la línea. a-Zcomo un rango no tiene mucho sentido, incluso en entornos locales basados ​​en ASCII. Tenga en cuenta que -oes una extensión GNU.
Stéphane Chazelas
0

Si calificas la palabra para que signifique cualquier secuencia de 1 o más caracteres que no estén en blanco, entonces la respuesta es definitivamente sí, y también se hace de manera muy simple. Esto se debe [[:blank:]]*, y [^[:blank:]]*son complementos booleanas y - siempre todos los caracteres de una cadena están completos - [[:blank:]]*T [^[:blank:]]*puede describir cualquier cadena sea posible, de la misma manera .*lo hace.

Si existe un carácter incompleto o una secuencia de bytes inválida dentro de una cadena, ninguno de los dos puede describirla correctamente de principio a fin, como a veces puede ocurrir al interpretar una cadena en la codificación incorrecta. Para garantizar un carácter completo por byte en cualquier cadena, la configuración regional de C se puede forzar como:

LC_ALL=C sed ...

... lo que evitaría cualquier problema que describa la cadena de la cabeza a la cola con un patrón todo incluido como .*o([ ]*[^ ]*)*

Un patrón completamente complementario puede repetir tantas veces como sea necesario de izquierda a derecha la longitud de cualquier cadena para aterrizar en la última aparición posible sin interrupción en el patrón. Este es, definitivamente, un lenguaje regular.

BRE:

sed 's/\(\([^[:blank:]]*\)[[:blank:]]*\)*/\2/'

ANTES DE:

sed -E 's/(([^[:blank:]]*)[[:blank:]]*)*/\2/'

Ambas versiones seguirán imprimiendo líneas en blanco, y esto se debe a que la *estrella de Kleene coincide con cero o más ocurrencias de un patrón. Primero coincide con cero o más caracteres no en blanco, luego cero o más caracteres en blanco, luego cero o más ocurrencias de las coincidencias agrupadas hasta que coincida con la cadena en su totalidad.

Una vez hecho todo esto, la magia ocurre en el reemplazo: las referencias devueltas por los grupos \1y \2son las últimas ocurrencias de cada uno. Entonces, cuando se realiza el reemplazo, toda la cadena se reemplaza con solo la última aparición en una línea de cero o más caracteres no en blanco, o el subgrupo \2.

Por supuesto, esto funciona para cualquier cadena posible , incluso una vacía, lo que significa que ambos formularios imprimirán caracteres de nueva línea para líneas que contienen solo caracteres en blanco o ninguno. Para manejar esto, hay un par de cosas que puede hacer, pero primero hagamos que la clase de caracteres sea un poco más fácil de escribir:

b='[:blank:]'

Ahora, para imprimir solo si una línea contiene uno o más caracteres no en blanco, puede hacer:

BRE:

sed -n "s/\(\([^$b]*\)[$b]*\)*/\2/;/./p"

ANTES DE:

sed -En "/[^$b]/s/(([^$b]*)[$b]*)*/\2/p"
  1. Caso BRE: la sustitución siempre se realiza y solo se imprimen los espacios de patrón con al menos un carácter restante.
  2. Caso ERE: la sustitución solo se intenta en un espacio de patrón que contiene al menos un carácter no en blanco.

Cualquiera de las formas funcionará con cualquiera de los métodos, siempre que la sintaxis sea correcta.

El -ninterruptor deshabilita la impresión automática del espacio del patrón, y el pindicador de la s///ubicación o los comandos de /dirección /imprime sus resultados solo si tiene éxito.

Esta misma lógica se puede aplicar para obtener cualquier {num}ocurrencia, como:

BRE:

sed -n "s/\([$b]*\([^$b]\{1,\}\)\)\{num\}.*/\2/p"

ANTES DE:

sed -En "s/([$b]*([^$b]+)){num}.*/\2/p"

... donde las numdos expresiones regulares se pueden reemplazar con un número para imprimir solo la {num}aparición especificada de una secuencia de caracteres no en blanco. Aquí se usa una forma ligeramente diferente para garantizar que el recuento no esté sesgado para el espacio inicial en una cadena.

Tenga en cuenta que el -Ecambio ERE a sedes compatible con las versiones BSD y GNU, aunque todavía no es la sintaxis estándar POSIX.

mikeserv
fuente
Buenas explicaciones, buen truco, pero tenga en cuenta que no funcionará con implementaciones tradicionales sed (como Solaris / usr / bin / sed) y va a ser más costoso que el enfoque más sencillo (agota la memoria con líneas de entrada de más de 25 caracteres de largo con el sed_su3del cofre de herramientas Heirloom, por ejemplo). Entonces, aunque me gusta la respuesta, no recomendaría ese enfoque.
Stéphane Chazelas
Tampoco parece funcionar en FreeBSD.
Stéphane Chazelas
@ StéphaneChazelas: sí, el rendimiento es realmente horrible para algo como esto, pero puede ser muy efectivo para detectar ocurrencias numeradas. Y para un caso de fin de línea s/.* \([^[:blank:]]\{1,\}\).*/\1/es mucho mejor, pero es más difícil cuando hay varias líneas involucradas. Sin embargo, el otro día descubrí que 's/\(\n\)*/\1/g;s/\n\(\n.*\)*/&&/[num];s///[samenum]puede apuntalar eso de manera bastante efectiva. De todos modos, siempre y cuando no haya un error evidente en la lógica, entonces estoy feliz, solo pensé que debí haberme perdido algo.
mikeserv 03 de
@ StéphaneChazelas - oh, y sobre los seds más viejos - eso es un poco extraño - debería sonar de acuerdo con el estándar. xrat dice ... Los desarrolladores estándar consideraron el comportamiento histórico común, que admitía "\n*", pero no "\n\{min,max\}", "\(...\)*", o "\(...\)\{min,max\}"como un resultado no intencional de una implementación específica, y admitieron tanto la duplicación como las expresiones de intervalo después de subexpresiones y referencias posteriores.
mikeserv 03 de
@ StéphaneChazelas - Y el estándar dice ... Si la subexpresión a la que hace referencia la referencia inversa coincide con más de una cadena debido a un asterisco ( '*' )o una expresión de intervalo (ver ítem (5)), la referencia inversa coincidirá con la última (más a la derecha ) de estas cadenas. Estoy bastante seguro de que probé esto sin minisedembargo, ciertamente estaba probando algo extraño minisedel otro día, de todos modos.
mikeserv 03 de
-1

Si. El siguiente comando sed primero elimina todos los espacios en blanco al final ( s/ *$//) y luego todo hasta el último espacio en blanco ( s/.* //). Probablemente valga la pena reemplazar el espacio [[:blank:]]en blanco literal con el fin de capturar pestañas y otros caracteres similares al espacio.

$ echo "  aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc   " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "aaa bbb cc" | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "  cc  " | sed -e 's/ *$//' -e 's/.* //'
cc
$ echo "cc" | sed -e 's/ *$//' -e 's/.* //'
cc
mkalkov
fuente
-1
cat file_name | rev | cut -f1 -d ' ' | rev
COMO LO
fuente