Sadece son sütunu nasıl yazdırılır?


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

Bazı "MAGIC" in bu çıktısını almasını nasıl sağlayabilirim ?:

three
six
nine

GÜNCELLEME: Bu özel şekilde ihtiyacım yok, genel bir çözüme ihtiyacım var, böylece bir satırda kaç sütun olursa olsun, örneğin: awk her zaman son sütunu görüntüler.


2
Lance sormadan önce sorularınızı araştırın. Yayınlarınızın konu satırı için google'da arama yapmak, snippents’taki cevabı gösterir. "Son sütun awk" kelimesini aramak, sonuç 1'den başlayarak birçok harika cevap verir. Ayrıca, bu 5 dakikalık awk astar , ileride nelerin mümkün olduğunu bilmeniz için baştan sona okumaya değer.
Caleb,

Yanıtlar:


6

Sadece , veya 'bash'olmadan da yapılabilir :'sed''awk''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, ya da girişinizin gerçekte boşlukla ayrıldığını varsayarsak:... | while read -r line; do echo ${line##* }; done
glenn jackman

@glenn: Bu benim ilk fikrimdi, ancak 'okuma' kılavuzunu okuduğumda, yararlı bulduğum bu dizi fonksiyonunu gördüm. Sağdan indeksli herhangi bir alanı vermek için kolayca değiştirilebilir.
jfg956

2
bashdizi indeksi aritmetik değerlendirmenin konusu, bu yüzden echo ${line[nb - 1]}yeter. Hakkında konuşurken gibi bash, sadece “nb” şeyler atlayabilirsiniz: echo ${line[-1]}. Daha sonra bir daha taşınabilir bir alternatif: echo ${line[@]: -1]}. (Bkz. Stephane Chazelas'ın başka bir yerdeki negatif endeksler hakkındaki yorumu .)
manatwork

53

Deneyin:

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


lütfen awk'nin 99 alanla sınırlı olduğunu unutmayın ...: / Bu sadece son günlerde beni ısırır ( ps -ef | awk '{ print $NF }'bazı satırları kesilmiş ...) Perl'in bu sınırlaması yoktur. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Geleneksel Awk’in bir kayıtta 99 alan sınırı vardır. Tru64’ler gibi bazı Awk uygulamalarından sonra başvurmasanız bile girişi bölüşün betiğin herhangi bir alanına, bu sorunu aşmak için, 'FS'yi olağandışı bir karaktere ayarlayın ve bölmeyi kullanın. ")
Olivier Dulac

@OlivierDulac hangi awkuygulamaların bu sınırlamaya sahip? Onu hiç görmedim. Boğulacağım mawkirade 32768ama benim gawkve igawkmilyonlarca insanla mutlu bir şekilde başa çıkabilirim. Meşgul kutularım bile awkmilyonlarca insanla başa çıkabilir. Hiç karşılaşmadım awk, 100 alanla başa çıkamayan bir şey, sonuçta bu küçük bir sayı. Bilginin hala alakalı olduğundan emin misin? Solaris'te bile mi?
terdon

@terdon, yorumumdaki bağlantıları görün ^^ (ve inan bana, bazı "eski" sistemler bazı ortamlarda loooooooog zamanlarında hayatta kalabilirler. Bazıları üzerinde, katran hapilly "/" 'ye göre, bash' ın bir kısmı yararlı değildir. yerleşikler (örneğin, $ BASH_SOURCE gibi), NF> 99’da awk boğulma ... :()
Olivier Dulac

@ OliverDulac yeterince adil. Sadece rastlamadım. Umarım bugün ufukta nadir görülür, çünkü 99 ufak bir sayıdır.
terdon

14

Düşündüğünden daha kolay.

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

11

Deneyin grep(daha kısa / basit, ancak awkregex kullanımından 3 kat daha yavaş ):

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

Veya ex(daha yavaş, ancak bitirdikten sonra tüm arabelleği yazdırır, yerinde sıralanması veya düzenlenmesi gerektiğinde daha yararlı olur):

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

Yerinde değiştirmek için -sc'%p|q!'ile değiştirin -scwq.

Veya bash:

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

performans

Oluşturulan 1GB dosya:

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

Ayrıştırma zamanı istatistiklerini gerçekleştirdim (~ 3x koştum ve en düşük seviyeyi aldım, MBP OS X'te test edildi):

  • kullanarak awk:

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

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
    
  • rev+ kullanarak 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
    
  • kullanarak 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
    
  • kullanarak 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]'

Anahtar noktalar:, dizideki -aautosplit alanları @F; -lkeser $/(giriş kayıt ayırıcısı) ve kaydeder $\(çıkış kayıt ayırıcısı). Sekizli bir sayı belirtilmediğinden -l, $/baskıya orijinal uygulanır (satır sonları); -ndöngü kodu; -ehemen ardından kodu yürütün. Bakın man perlrun.
Jonathan Komar

5

Ayrıca kullanılarak yapılabilir 'sed':

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

Güncelleştirme:

veya daha sade bir şekilde:

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

daha basitçe daha iyidir!
mdpc

@mdpc: Katılıyorum, ancak daha karmaşık olan çözüm zaten yayınlandığı için, kalmaya karar verdim.
jfg956

5

Veya kullanarak cut:

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

bu, 'genel çözüm' gereksinimini karşılamamasına rağmen. revİki kere kullanarak bunu da çözebiliriz:

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

'Rev' in tüm Unix'te (AIX, Solaris, ...) bulunabileceğini ya da tüm Linux'ta kurulduğunu sanmıyorum ama güzel bir alternatif çözüm.
jfg956,

1
Çift rev için +1, ancak bir not olarak, revbildiğim kadarıyla 'geniş' karakterlerle, sadece tek baytlılarla çalışmaz.
Marcin

2

Kullanımı awken az bir sütun varsa ilk kontrol edebilirsiniz size.

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

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

3
Ya da daha az ayrıntılı awk 'NF{print $NF}'.
Manatwork

1

Perl'de bu şu şekilde yapılabilir:

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

çıktı:

$ perl last.pl
5
$

Ayrıca bir dosya üzerinden döngüde bulunabilir, her şeyde budget.dat adlı bir dosyayı ayrıştırmak için yazdığım örnek bir komut dosyası bulunur.

budget.dat içindeki örnek veriler:

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

(sadece 2 sütununu değil, yalnızca "son" sütunu yakalamak için gerekli olduğumu görebilirsiniz)

Senaryo:

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

Başka birinin de aynı şeyi yorumladığını fark ettim, bunun için üzgünüm, en azından benim de birkaç örneğim var, umarım yine de tartışmaya katkıda bulunur.
urbansumo
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.