Como imprimir apenas a última coluna?

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

como faço para "MAGIC" obter essa saída ?:

three
six
nine

UPDATE: Não preciso dessa maneira específica, preciso de uma solução geral para que não importa quantas colunas estejam seguidas, por exemplo: awk sempre exibe a última coluna.

LanceBaynes
fonte
2
Lance, pesquise suas perguntas antes de fazer. Pesquisando no google a linha de assunto de suas postagens mostra a resposta nos snippents. Pesquisando "awk last column" fornece várias ótimas respostas, começando com o resultado 1. Além disso, vale a pena ler este iniciador de awk de 5 minutos durante todo o caminho para que você saiba o que é possível no futuro.
Caleb

Respostas:

6

Ele pode até mesmo ser feito apenas com 'bash', sem 'sed', 'awk'ou '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
jfg956
fonte
Hmm, ou ainda, assumindo sua entrada é realmente separados por espaço:... | while read -r line; do echo ${line##* }; done
Glenn Jackman
@glenn: Essa foi minha primeira idéia, mas quando li o manual 'read', vi essa função de matriz que achei útil. Também pode ser facilmente modificado para fornecer qualquer campo indexado à direita.
Jfg956
2
bashO índice de matriz é objeto de avaliação aritmética, o que echo ${line[nb - 1]}é suficiente. Como falar bash, você pode simplesmente ignorar as coisas “NB”: echo ${line[-1]}. Uma alternativa mais portátil da tarde: echo ${line[@]: -1]}. (Veja comentário Stephane Chazelas' em índices negativos em outros lugares.)
manatwork
53

Experimentar:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'
Sean C.
fonte
Atualizei o Q
LanceBaynes
observe que o awk está limitado a 99 campos ...: / Isso me incomodou nos últimos dias ( ps -ef | awk '{ print $NF }'algumas linhas foram truncadas ...) O Perl não tem essa limitação. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "O Awk tradicional tem um limite de 99 campos em um registro. Como algumas implementações do Awk, como as do Tru64, dividem a entrada mesmo que você não se refira para qualquer campo do script, para contornar esse problema, defina 'FS' com um caractere incomum e use split. ")
Olivier Dulac
@OlivierDulac que awkimplementações têm essa limitação? Eu nunca vi isso. Minha mawkvontade engasga, 32768mas a minha gawke igawkpode lidar com milhões felizes. Até o meu busybox awkpode lidar com milhões. Eu nunca me deparei com um awkque não possa lidar com 100 campos, afinal, é um número pequeno. Você tem certeza de que as informações ainda são relevantes? Mesmo no Solaris?
terdon
@terdon, veja os links no meu comentário ^^ (e acredite, algum sistema "legado" pode sobreviver a um tempo loooooooog em alguns ambientes. em alguns, o alcatrão é extraído para "/", o bash não tem alguns dos úteis builtins (nem $ BASH_SOURCE, por exemplo), awk engasgar com NF> 99, etc ... :()
Olivier Dulac
@OlivierDulac justo o suficiente. Eu simplesmente não me deparei com isso. Espero que seja muito raro hoje, já que 99 é um número pequeno.
terdon
14

É mais fácil do que você pensa.

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

Tente grep(mais curto / mais simples, mas 3x mais lento que o awkuso de expressões regulares):

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

Ou ex(ainda mais lento, mas imprime todo o buffer após o término, mais útil quando ele precisa ser classificado ou editado no local):

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

Para mudar no local, substitua -sc'%p|q!'por -scwq.

Ou bash:

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

atuação

Dado o arquivo de 1 GB gerado via:

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

Eu executei as estatísticas do tempo de análise (correu ~ 3x e fiz a menor, testei no MBP OS X):

  • usando awk:

    $ time awk '{print $NF}' datafile > /dev/null
    real    0m12.124s
    user    0m10.704s
    sys 0m0.709s
  • usando 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
  • usando 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
  • usando 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
  • usando bash:

    $ time while read line; do arr=($line); echo ${arr[-1]}; done < datafile > /dev/null
    real    9m42.807s
    user    8m12.553s
    sys 1m1.955s
kenorb
fonte
6
... | perl -lane 'print $F[-1]'
Glenn Jackman
fonte
Pontos principais -a:, campos de divisão automática na @Fmatriz; -lcorta $/(separador de registro de entrada) e o salva em $\(separador de registro de saída). Como nenhum número octal é fornecido -l, o original $/é aplicado na impressão (finais de linha); -ncódigo de loop; -eexecute o código imediatamente a seguir. Veja man perlrun.
Jonathan Komar 24/09
5

Também pode ser feito usando 'sed':

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

Atualizar:

ou mais simplesmente:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'
jfg956
fonte
mais simplesmente é melhor!
Mdpc
@mdpc: Eu concordo, mas como a solução mais complicada já foi publicada, decidi mantê-la.
jfg956
5

Ou usando cut:

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

embora isso não atenda ao requisito de 'solução geral'. Usando revduas vezes, podemos resolver isso também:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev
Tim
fonte
Eu não acho que 'rev' possa ser encontrado em todo o Unix (AIX, Solaris, ...) ou esteja instalado em todo o Linux, mas uma boa solução alternativa.
Jfg956
11
+1 para rotação dupla, mas, como nota lateral, revnão funciona com caracteres "largos", apenas caracteres de byte único, tanto quanto eu sei.
Marcin
2

Usando awkvocê, você pode primeiro verificar se há pelo menos uma coluna.

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

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'
gezu
fonte
3
Ou menos verbalmente awk 'NF{print $NF}'.
manatwork
1

Em perl, isso pode ser feito da seguinte maneira:

#!/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";

saída:

$ perl last.pl
5
$

Você também pode percorrer um arquivo, veja um exemplo de script que escrevi para analisar um arquivo chamado budget.dat

dados de exemplo em budget.dat:

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

(você pode ver que eu precisava capturar apenas a coluna "última", não apenas a coluna 2)

O script:

#!/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";
urbansumo
fonte
Percebi que alguém já havia comentado o mesmo, desculpe-me por isso, pelo menos tenho alguns exemplos também, espero que isso acrescente à discussão de qualquer maneira.
Urbansumo # 29/15