Bagaimana cara mencetak hanya kolom terakhir?

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

bagaimana saya bisa melakukan beberapa "MAGIC" untuk mendapatkan hasil ini ?:

three
six
nine

UPDATE: Saya tidak membutuhkannya dengan cara khusus ini, saya membutuhkan solusi umum sehingga tidak peduli berapa banyak kolom berturut-turut, misalnya: awk selalu menampilkan kolom terakhir.

LanceBaynes
sumber
2
Tombak harap teliti pertanyaan Anda sebelum bertanya. Mencari google untuk baris subjek posting Anda menunjukkan jawabannya di snippents. Pencarian "awk last kolom" memberikan beberapa jawaban bagus dimulai dengan hasil 1. Juga, primer awk 5 menit ini layak dibaca sampai selesai sehingga Anda tahu apa yang mungkin terjadi di masa depan.
Caleb

Jawaban:

6

Bahkan dapat dilakukan hanya dengan 'bash', tanpa 'sed', 'awk'atau '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
sumber
Hmm, atau juga, dengan asumsi input Anda sebenarnya dipisahkan oleh ruang:... | while read -r line; do echo ${line##* }; done
glenn jackman
@glenn: Ini adalah ide pertama saya, tetapi ketika saya membaca manual 'read', saya melihat fungsi array yang menurut saya berguna. Itu juga dapat dengan mudah dimodifikasi untuk memberikan bidang apa saja yang diindeks dari kanan.
jfg956
2
bashindeks array adalah subjek evaluasi aritmatika, jadi echo ${line[nb - 1]}sudah cukup. Seperti berbicara tentang bash, Anda hanya dapat melewati “nb” hal: echo ${line[-1]}. Sebuah alternatif yang lebih portabel dari kemudian: echo ${line[@]: -1]}. (Lihat komentar Stephane Chazelas tentang indeks negatif di tempat lain.)
manatwork
53

Mencoba:

echo -e 'one two three\nfour five six\nseven eight nine' | awk '{print $NF}'
Sean C.
sumber
Saya memperbarui Q
LanceBaynes
harap dicatat bahwa awk terbatas pada 99 bidang ...: / Ini hanya menggigit saya dalam beberapa hari terakhir ( ps -ef | awk '{ print $NF }'memiliki beberapa baris terpotong ...) Perl tidak memiliki batasan itu. ( gnu.org/software/autoconf/manual/autoconf-2.67/html_node/… : "Tradisional Awk memiliki batas 99 bidang dalam catatan. Karena beberapa implementasi Awk, seperti Tru64, membagi input bahkan jika Anda tidak merujuk ke bidang apa pun dalam skrip, untuk menghindari masalah ini, atur 'FS' ke karakter yang tidak biasa dan gunakan split. ")
Olivier Dulac
@OlivierDulac awkimplementasi apa yang memiliki batasan itu? Saya belum pernah melihatnya. Saya mawkakan tersedak 32768tapi saya gawkdan igawkbisa berurusan dengan jutaan bahagia. Bahkan kotak sibuk saya awkdapat menangani jutaan. Saya tidak pernah menemukan awkyang tidak bisa menangani 100 bidang, itu adalah jumlah yang kecil. Apakah Anda yakin bahwa informasi itu masih relevan? Bahkan di Solaris?
terdon
@terdon, lihat tautan di komentar saya ^^ (dan percayalah, beberapa sistem "warisan" dapat bertahan waktu loooooooog di beberapa lingkungan. pada beberapa, ekstrak tar hapilly untuk "/", bash tidak memiliki beberapa berguna builtins (atau $ BASH_SOURCE, misalnya), awk tercekik di NF> 99, dll ... :()
Olivier Dulac
@OlivierDulac cukup adil. Aku belum menemukannya. Saya harap hari ini sangat jarang karena 99 adalah angka kecil.
terdon
14

Lebih mudah dari yang Anda pikirkan.

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

Coba grep(lebih pendek / sederhana, tetapi 3x lebih lambat daripada awkkarena penggunaan regex):

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

Atau ex(bahkan lebih lambat, tetapi mencetak seluruh buffer setelah selesai, lebih berguna ketika perlu disortir atau diedit di tempat):

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

Untuk mengganti di tempat, ganti -sc'%p|q!'dengan -scwq.

Atau bash:

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

Performa

Mengingat file 1GB yang dihasilkan melalui:

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

Saya telah melakukan statistik waktu parsing (berlari ~ 3x dan mengambil yang terendah, diuji pada MBP OS X):

  • menggunakan awk:

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

    $ time perl -lane 'print $F[-1]' datafile > /dev/null
    real    0m48.292s
    user    0m47.601s
    sys 0m0.396s
  • menggunakan 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
  • menggunakan 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
  • menggunakan 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
sumber
6
... | perl -lane 'print $F[-1]'
glenn jackman
sumber
Poin kunci -a:, bidang autosplit ke dalam @Farray; -lmematikan $/(pemisah rekaman input) dan menyimpannya ke $\(pemisah rekaman keluaran). Karena tidak ada nomor oktal yang disediakan -l, dokumen asli $/diterapkan dalam bentuk cetak (ujung garis); -nkode loop; -esegera jalankan kode. Lihat man perlrun.
Jonathan Komar
5

Ini juga dapat dilakukan dengan menggunakan 'sed':

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

Memperbarui:

atau lebih sederhana:

echo -e 'one two three\nfour five six\nseven eight nine' | sed -e 's/^.* //'
jfg956
sumber
lebih sederhana lebih baik!
mdpc
@ mdpc: Saya setuju, tetapi karena solusi yang lebih rumit sudah diposting, saya memutuskan untuk menyimpannya.
jfg956
5

Atau menggunakan cut:

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

walaupun ini tidak memenuhi persyaratan 'solusi umum'. Menggunakan revdua kali kita bisa menyelesaikan ini juga:

echo -e 'one two three\nfour five six\nseven eight nine' | rev | cut -f 1 -d' ' | rev
Tim
sumber
Saya tidak berpikir 'rev' dapat ditemukan di semua Unix (AIX, Solaris, ...) atau diinstal pada semua Linux, tetapi solusi alternatif yang bagus.
jfg956
1
+1 untuk putaran ganda, tetapi sebagai catatan tambahan, revtidak berfungsi dengan karakter 'lebar', hanya yang byte tunggal, sejauh yang saya tahu.
Marcin
2

Menggunakan awkAnda dapat terlebih dahulu memeriksa apakah ada setidaknya satu kolom.

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

echo 1 2 3 | awk '{if (NF >= 1) print $NF}'
gezu
sumber
3
Atau kurang secara lisan awk 'NF{print $NF}'.
manatwork
1

Dalam perl ini dapat dilakukan sebagai berikut:

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

keluaran:

$ perl last.pl
5
$

Anda juga dapat mengulang melalui file, inilah contoh skrip yang saya tulis untuk mem-parsing file bernama budget.dat

contoh data dalam budget.dat:

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

(Anda dapat melihat saya perlu menangkap hanya kolom "terakhir", bukan kolom 2 saja)

Naskah:

#!/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
sumber
Saya menyadari ada orang lain yang sudah berkomentar yang sama, maaf soal itu, setidaknya saya punya beberapa contoh juga, semoga saja itu menambah diskusi.
urbansumo