Pode até 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
echo -e 'one two three\nfour five six\nseven eight nine'
one two three
four five six
seven eight nine
como posso fazer um "MAGIC" obter essa saída?:
three
six
nine
ATUALIZAÇÃO: Eu não preciso disso desta maneira específica, eu preciso de uma solução geral para que não importa quantas colunas estão em uma linha, por exemplo: o awk sempre exibe a última coluna.
Tente:
echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'
É mais fácil do que você pensa.
$ echo one two three | awk '{print $NF}'
three
Teste grep
(mais curto / mais simples, mas 3x mais lento que awk
devido ao uso de regex):
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 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 alterar no local, substitua -sc'%p|q!'
por -scwq
.
Ou bash
:
while read line; do arr=($line); echo ${arr[-1]}; done < someinput
Dado o arquivo gerado de 1 GB via:
$ hexdump -C /dev/urandom | rev | head -c1G | pv > datafile
Eu executei as estatísticas de tempo de análise (correu ~ 3x e levou o menor, testado 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
... | perl -lane 'print $F[-1]'
Isso também pode ser feito usando 'sed'
:
echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* \([^ ]*\)$//'
Atualização:
ou mais simplesmente:
echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'
Usando awk
, 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}'
No perl isso pode ser feito da seguinte forma:
#!/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, aqui está um script de exemplo 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 "última" coluna, 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";
Tags text-processing awk