Làm thế nào để chỉ in cột cuối cùng?


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

Làm thế nào tôi có thể làm một số "MAGIC" để có được đầu ra này?:

three
six
nine

CẬP NHẬT: Tôi không cần nó theo cách cụ thể này, tôi cần một giải pháp chung để cho dù có bao nhiêu cột trong một hàng, ví dụ: awk luôn hiển thị cột cuối cùng.


2
Lance hãy nghiên cứu câu hỏi của bạn trước khi hỏi. Tìm kiếm google cho dòng chủ đề của bài viết của bạn cho thấy câu trả lời trong đoạn trích. Tìm kiếm "awk cột cuối cùng" cho một số câu trả lời tuyệt vời bắt đầu bằng kết quả 1. Ngoài ra, đoạn mồi awk 5 phút này đáng để đọc suốt để bạn biết những gì có thể trong tương lai.
Caleb

Câu trả lời:


6

Nó thậm chí có thể được thực hiện chỉ với 'bash', mà không 'sed', 'awk'hoặc '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, hoặc cũng có thể, giả sử đầu vào của bạn thực sự được phân tách bằng dấu cách:... | while read -r line; do echo ${line##* }; done
glenn jackman

@glenn: Đây là ý tưởng đầu tiên của tôi, nhưng khi tôi đọc hướng dẫn 'đọc', tôi thấy hàm mảng này mà tôi thấy hữu ích. Nó cũng có thể dễ dàng sửa đổi để cung cấp cho bất kỳ trường nào được lập chỉ mục từ bên phải.
jfg956

2
bashchỉ số mảng là đối tượng của đánh giá số học, như vậy echo ${line[nb - 1]}là đủ. Như đã nói bash, bạn có thể bỏ qua những điều nb nb echo ${line[-1]}. Một thay thế di động hơn về sau : echo ${line[@]: -1]}. (Xem bình luận của Stephane Chazelas về các chỉ số tiêu cực ở nơi khác.)
manatwork 18/03/13

53

Thử:

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

Tôi đã cập nhật Q
LanceBaynes

xin lưu ý rằng awk bị giới hạn ở 99 trường ...: / Điều này chỉ cắn tôi trong những ngày qua ( ps -ef | awk '{ print $NF }'đã cắt một số dòng ...) Perl không có giới hạn đó. ( Gnu.org/software/autoconf/manual/autoconf-2.67/html_node/... : "truyền thống AWK có một giới hạn của 99 lĩnh vực trong một bản ghi từ một số triển khai AWK, như Tru64 của, chia đầu vào ngay cả khi bạn không tham khảo. đến bất kỳ trường nào trong tập lệnh, để khắc phục sự cố này, đặt 'FS' thành một ký tự bất thường và sử dụng phân tách. ")
Olivier Dulac

@OlivierDulac những gì awktriển khai có giới hạn đó? Tôi chưa bao giờ nhìn thấy nó. Ý chí của tôi mawksẽ nghẹt thở 32768nhưng tôi gawkigawkcó thể giải quyết hàng triệu hạnh phúc. Ngay cả bận rộn của tôi awkcó thể đối phó với hàng triệu. Rốt cuộc, tôi chưa bao giờ bắt gặp một awklĩnh vực không thể đối phó với 100 lĩnh vực, đó là một con số rất nhỏ. Bạn có chắc chắn rằng thông tin vẫn còn liên quan? Ngay cả trên Solaris?
terdon

@terdon, hãy xem các liên kết trong nhận xét của tôi ^^ (và tin tôi đi, một số hệ thống "di sản" có thể tồn tại trong một thời gian loooooooog trong một số môi trường. nội dung (chẳng hạn như $ BASH_SOURCE chẳng hạn), awk sặc trên NF> 99, v.v ... :()
Olivier Dulac

@OlivierDulac đủ công bằng. Tôi không đi ngang qua nó. Tôi hy vọng nó hiếm khi biến mất ngày hôm nay vì 99 là một con số nhỏ.
terdon

14

Nó dễ hơn bạn nghĩ.

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

11

Hãy thử grep(ngắn hơn / đơn giản hơn, nhưng chậm hơn 3 lần so với awkvì sử dụng regex):

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

Hoặc ex(thậm chí chậm hơn, nhưng nó in toàn bộ bộ đệm sau khi hoàn tất, hữu ích hơn khi cần sắp xếp hoặc chỉnh sửa tại chỗ):

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

Để thay đổi tại chỗ, thay thế -sc'%p|q!'bằng -scwq.

Hoặc bash:

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

Hiệu suất

Cho tệp 1GB được tạo thông qua:

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

Tôi đã thực hiện thống kê thời gian phân tích cú pháp (chạy ~ 3x và ở mức thấp nhất, được thử nghiệm trên MBP OS X):

  • sử dụng awk:

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

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

Các điểm chính : -a, các trường tự động thành @Fmảng; -ltắt $/(bộ tách bản ghi đầu vào) và lưu nó vào $\(bộ tách bản ghi đầu ra). Bởi vì không có số bát phân nào được cung cấp -l, bản gốc $/được áp dụng trong bản in (kết thúc dòng); -nmã vòng lặp; -ethực thi mã ngay sau đây. Xem man perlrun.
Jonathan Komar

5

Nó cũng có thể được thực hiện bằng cách sử dụng 'sed':

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

Cập nhật:

hoặc đơn giản hơn:

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

Đơn giản hơn là tốt hơn!
mdpc

@mdpc: Tôi đồng ý, nhưng vì giải pháp phức tạp hơn đã được đăng, tôi quyết định giữ nó.
jfg956

5

Hoặc sử dụng cut:

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

mặc dù điều này không thỏa mãn yêu cầu 'giải pháp chung'. Sử dụng revhai lần chúng ta cũng có thể giải quyết điều này:

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

Tôi không nghĩ rằng 'rev' có thể được tìm thấy trên tất cả Unix (AIX, Solaris, ...) hoặc được cài đặt trên tất cả Linux, nhưng giải pháp thay thế tuyệt vời.
jfg956

1
+1 cho vòng quay kép, nhưng như một ghi chú bên cạnh, revkhông hoạt động với các ký tự 'rộng', chỉ có các byte đơn, theo như tôi biết.
Marcin

2

Sử dụng awktrước tiên bạn có thể kiểm tra nếu có ít nhất một cột.

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

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

3
Hoặc ít bằng lời nói awk 'NF{print $NF}'.
manatwork 18/03/13

1

Trong perl điều này có thể được thực hiện như sau:

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

đầu ra:

$ perl last.pl
5
$

Bạn cũng có thể lặp qua một tệp, đây là một đoạn mã ví dụ mà tôi đã viết để phân tích một tệp có tên là bank.dat

dữ liệu ví dụ trong ngân sách.dat:

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

(bạn có thể thấy tôi chỉ cần chụp cột "cuối cùng", không phải cột 2)

Kịch bản:

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

Tôi nhận ra một người khác đã nhận xét tương tự, xin lỗi về điều đó, ít nhất tôi cũng có một vài ví dụ, hy vọng nó sẽ thêm vào cuộc thảo luận.
urbansumo
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.