如何只打印最后一列?


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

我该如何做一些“魔术”来获得此输出?:

three
six
nine

更新:我不需要这种特定的方式,我需要一个通用的解决方案,以便无论一行中有多少列,例如:awk始终显示最后一列。


2
兰斯请先研究您的问题再询问。在google中搜索您帖子的主题行会在摘要中显示答案。搜索“ awk last column”会从结果1开始给出几个很好的答案。而且,这个5分钟的awk入门值得一通阅读,因此您知道将来可能会发生什么。
卡莱布

Answers:


6

它甚至可以只完成'bash',无'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

嗯,或者也假设您的输入实际上是用空格分隔的:... | while read -r line; do echo ${line##* }; done
glenn jackman 2011年

@glenn:这是我的第一个主意,但是当我阅读“阅读”手册时,我看到了这个数组函数,我发现它很有用。也可以轻松地对其进行修改,以提供从右开始索引的任何字段。
jfg956

2
bash数组索引是算术评估的主题,因此echo ${line[nb - 1]}就足够了。说到这里bash,您可以跳过“ nb”内容:echo ${line[-1]}。后面的一种更可移植的替代方法:echo ${line[@]: -1]}。(请参阅Stephane Chazelas对其他地方的负面指数的评论。)
manatwork 2013年

53

尝试:

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

我更新了Q
LanceBaynes

请注意,awk仅限于99个字段...:/在过去的几天里这只是在咬我(ps -ef | awk '{ print $NF }'某些行被截断了……)Perl没有此限制。(gnu.org/software/autoconf/manual/autoconf-2.67/html_node/…:“传统Awk在记录中最多可包含99个字段。由于某些Awk实现(例如Tru64的实现)会拆分输入,即使您未引用到脚本中的任何字段,以解决此问题,请将“ FS”设置为不寻常的字符并使用split。”)
Olivier Dulac 2014年

@OlivierDulac有哪些awk限制?我没看过 我mawk会窒息32768,但我gawkigawk可以处理数以百万计愉快。甚至我的忙碌箱awk也可以应付数百万。awk毕竟,我从来没有遇到过无法处理100个字段的情况。您确定信息仍然有用吗?即使在Solaris上?
terdon

@terdon,请参阅我的评论中的链接^^(并且相信我,某些“旧版”系统可以在某些环境中幸免于难。)在某些情况下,tar hapilly提取为“ /”,bash没有一些有用的功能内置函数(例如,也没有$ BASH_SOURCE),NF> 99上的awk扼流圈等等::()
Olivier Dulac

@OlivierDulac足够公平。我只是没有遇到过。我希望它现在几乎消失,因为99是一个很小的数字。
terdon

14

它比您想象的要容易。

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

11

尝试grep(更短/更简单,但比awk使用正则表达式慢3倍):

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

ex(速度更慢,但是它会在完成后打印整个缓冲区,在需要就位排序或编辑时更有用):

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

要就地更改,请替换-sc'%p|q!'-scwq

bash

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

性能

通过以下方式生成1GB文件:

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

我已经执行了解析时间统计信息(运行了大约3倍,并且在MBP OS X上测试的时间最低):

  • 使用awk

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

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

关键点:-a,将字段自动拆分为@F数组;-l砍掉$/(输入记录分隔符)并将其保存到$\(输出记录分隔符)。因为没有随提供的八进制数字-l,所以原件$/应用于印刷(行尾);-n循环代码;-e此后立即执行代码。请参阅man perlrun
乔纳森·科马尔

5

也可以使用'sed'

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

更新:

或更简单地说:

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

更简单是更好!
mdpc

@mdpc:我同意,但是由于已经发布了更复杂的解决方案,因此我决定保留它。
jfg956

5

或使用cut

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

尽管这不满足“一般解决方案”的要求。使用rev两次,我们也可以解决此问题:

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

我不认为'rev'可以在所有Unix(AIX,Solaris等)上找到,也不能在所有Linux上都安装,但是不错的替代解决方案。
jfg956

1
+1代表双重修订,但rev据我所知,不适用于“宽”字符,仅适用于单字节字符。
Marcin 2012年

2

使用,awk您可以首先检查是否至少有一列。

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

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

3
或更不冗长awk 'NF{print $NF}'
manatwork 2013年

1

在perl中,可以按照以下步骤进行操作:

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

输出:

$ perl last.pl
5
$

您还可以遍历文件,这是我编写的一个示例脚本,用于解析名为budget.dat的文件。

budget.dat中的示例数据:

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

(您可以看到我只需要捕获“最后一个”列,而不必捕获第2列)

剧本:

#!/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
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.