¿Cómo imprimir solo la última columna?


25
echo -e 'one two three\nfour five six\nseven eight nine'
one two three
four five six
seven eight nine

¿Cómo puedo hacer algo de "MAGIA" para obtener esta salida ?:

three
six
nine

ACTUALIZACIÓN: No lo necesito de esta manera específica, necesito una solución general para que no importa cuántas columnas estén en fila, por ejemplo: awk siempre muestra la última columna.


2
Lance, investiga tus preguntas antes de preguntar. Al buscar en Google la línea de asunto de sus publicaciones, se muestra la respuesta en los fragmentos. La búsqueda en "awk last column" ofrece varias respuestas excelentes que comienzan con el resultado 1. Además, vale la pena leer este manual de awk de 5 minutos hasta el final para que sepa lo que es posible en el futuro.
Caleb

Respuestas:


6

Incluso se puede hacer sólo con 'bash', y sin 'sed', 'awk'o 'perl':

echo -e 'one two three\nfour five six\nseven eight nine' |
  while IFS=" " read -r -a line; do
    nb=${#line[@]}
    echo ${line[$((nb - 1))]}
  done

Hmm, o también, suponiendo que su entrada está realmente separada por espacios:... | while read -r line; do echo ${line##* }; done
Glenn Jackman

@glenn: Esta fue mi primera idea, pero cuando leí el manual 'leer', vi esta función de matriz que encontré útil. También se puede modificar fácilmente para dar cualquier campo indexado desde la derecha.
jfg956

2
bashEl índice de matriz es objeto de evaluación aritmética, por lo que echo ${line[nb - 1]}es suficiente. Como hablar bash, puede saltarse las cosas “NB”: echo ${line[-1]}. Una alternativa más portátil de la tarde: echo ${line[@]: -1]}. (Ver el comentario de Stephane Chazelas sobre índices negativos en otra parte.)
manatwork

53

Tratar:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'

Actualicé el Q
LanceBaynes el

tenga en cuenta que awk está limitado a 99 campos ...: / Esto solo me mordió en los últimos días ( ps -ef | awk '{ print $NF }'tenía algunas líneas truncadas ...) Perl no tiene esa limitación. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Awk tradicional tiene un límite de 99 campos en un registro. Dado que algunas implementaciones de Awk, como Tru64, dividen la entrada incluso si no hace referencia a cualquier campo en el script, para evitar este problema, establezca 'FS' en un carácter inusual y use división ".
Olivier Dulac

@OlivierDulac ¿qué awkimplementaciones tienen esa limitación? Nunca lo he visto. Mi mawkvoluntad se ahoga 32768pero mi gawky igawkpuede lidiar con millones felizmente. Incluso mi busybox awkpuede lidiar con millones. Nunca me he encontrado con un awkque no pueda manejar 100 campos, después de todo es un número muy pequeño. ¿Estás seguro de que la información sigue siendo relevante? ¿Incluso en Solaris?
terdon

@terdon, mira los enlaces en mi comentario ^^ (y créeme, algún sistema "heredado" puede sobrevivir un tiempo muuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu ... incorporados (ni $ BASH_SOURCE, por ejemplo), awk ahogarse en NF> 99, etc ... :()
Olivier Dulac

@OlivierDulac lo suficientemente justo. Simplemente no lo he encontrado. Espero que sea muy raro hoy ya que 99 es un número muy pequeño.
terdon

14

Es más fácil de lo que piensas.

$ echo one two three | awk '{print $NF}'
three

11

Pruebe grep(más corto / más simple, pero 3 veces más lento que awkpor el uso de expresiones regulares):

grep -o '\S\+$' <(echo -e '... seven eight nine')

O ex(aún más lento, pero imprime todo el búfer después de terminar, más útil cuando necesita ser ordenado o editado en el lugar):

ex -s +'%s/^.*\s//g' -c'%p|q!' <(echo -e '... seven eight nine')
ex +'%norm $Bd0' -sc'%p|q!' infile

Para cambiar en el lugar, reemplace -sc'%p|q!'con -scwq.

O bash:

while read line; do arr=($line); echo ${arr[-1]}; done < someinput

Actuación

Dado el archivo generado de 1GB a través de:

$ hexdump -C /dev/urandom | rev | head -c1G | pv > datafile

He realizado las estadísticas de tiempo de análisis (ejecuté ~ 3x y tomé el más bajo, probado en MBP OS X):

  • utilizando awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
    
  • utilizando grep:

    $ time grep -o '\S\+$' datafile > /dev/null
    real    0m36.731s
    user    0m36.244s
    sys 0m0.401s
    
    $ time grep -o '\S*$' datafile > /dev/null
    real    0m40.865s
    user    0m39.756s
    sys 0m0.415s
    
  • utilizando perl:

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
    
  • usando rev+ cut:

    $ time (rev|cut -d' ' -f1|rev) < datafile > /dev/null
    $ time rev datafile | cut -d' ' -f1 | rev > /dev/null
    real    1m10.342s
    user    1m19.940s
    sys 0m1.263s
  • utilizando ex:

    $ time ex +'%norm $Bd0_' -sc'%p|q!' datafile > /dev/null
    real    3m47.332s
    user    3m42.037s
    sys 0m2.617s
    $ time ex +'%norm $Bd0' -sc'%p|q!' datafile > /dev/null
    real    4m1.527s
    user    3m44.219s
    sys 0m6.164s
    $ time ex +'%s/^.*\s//g' -sc'%p|q!' datafile > /dev/null
    real    4m16.717s
    user    4m5.334s
    sys 0m5.076s
  • utilizando bash:

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s

6
... | perl -lane 'print $F[-1]'

Puntos clave: -aautosplit campos en @Fmatriz; -lcorta $/(separador de registro de entrada) y lo guarda en $\(separador de registro de salida). Como no se proporciona ningún número octal -l, el original $/se aplica en forma impresa (terminaciones de línea); -ncódigo de bucle; -eejecutar código inmediatamente de aquí en adelante. Ver man perlrun.
Jonathan Komar

5

También se puede hacer usando 'sed':

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* \([^ ]*\)$/\1/'

Actualizar:

o más simplemente:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'

¡más simplemente es mejor!
mdpc

@mdpc: Estoy de acuerdo, pero como la solución más complicada ya estaba publicada, decidí mantenerla.
jfg956

5

O usando cut:

echo -e 'one two three\nfour five six\nseven eight nine' | cut -f 3 -d' '

aunque esto no satisface el requisito de "solución general". Usando revdos veces podemos resolver esto también:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev

No creo que 'rev' se pueda encontrar en todos los Unix (AIX, Solaris, ...) o esté instalado en todos los Linux, pero es una buena solución alternativa.
jfg956

1
+1 para doble rev, pero como nota al margen, revno funciona con caracteres 'anchos', solo de un solo byte, que yo sepa.
Marcin

2

Utilizando awk, primero puede verificar si hay al menos una columna.

echo | awk '{if (NF >= 1) print $NF}'

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'

3
O menos verbosamente awk 'NF{print $NF}'.
manatwork

1

En perl esto se puede hacer de la siguiente manera:

#!/usr/bin/perl

#create a line of arbitrary data
$line = "1 2 3 4 5";

# splt the line into an array (we call the array 'array', for lolz)
@array = split(' ', $line);

# print the last element in the array, followed by a newline character;
print "$array[-1]\n";

salida:

$ perl last.pl
5
$

También puede recorrer un archivo, aquí hay un script de ejemplo que escribí para analizar un archivo llamado budget.dat

datos de ejemplo en budget.dat:

Rent              500
Food              250
Car               300
Tax               100
Car Tax           120
Mag Subscription  15

(puede ver que necesitaba capturar solo la "última" columna, no solo la columna 2)

La secuencia de comandos:

#!/usr/bin/perl
$budgetfile = "budget.dat";
open($bf, $budgetfile)
        or die "Could not open filename: $filename $!";


print "-" x 50, "\n";
while ( $row = <$bf> ) {
        chomp $row;
        @r = split (' ', $row);
        print "$row ";
        $subtotal += $r[-1];
        print "\t$subtotal\n";
}
print "-" x 50, "\n";
print "\t\t\t Total:\t$subtotal\n\n";

Me di cuenta de que alguien más ya había comentado lo mismo, lo siento, al menos también tengo algunos ejemplos, espero que de todos modos se agregue a la discusión.
urbansumo
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.