如何使用awk在特定列后打印所有列?

108
在Shell中,当我需要特定列时,我会使用管道符号来调用 awk。例如,以下代码将打印第 9 列:
... | awk '{print $9}'

如何告诉awk打印包括第九列以及之后的所有列,而不仅仅是第九列?


1
可能是重复的问题:使用awk从第n列打印到最后一列 - user2350426
12个回答

108
awk '{ s = ""; for (i = 9; i <= NF; i++) s = s $i " "; print s }'

4
对于这段代码的一些微小改进:awk -v N=9 '{sep=""; for (i=N; i<=NF; i++) {printf("%s%s",sep,$i); sep=OFS}; printf("\n")}'简要翻译: 这是一段awk代码,用于打印第9个字段及其后面的所有字段,并使用空格分隔它们。这里对代码进行了一些微小的改进。 - glenn jackman
1
@SiegeX:它不会添加NUL字节,它会在每个空字段之间保留FS。 - Dennis Williamson
@Dennis 啊,原来是这样。如果我没有使用默认的FS,那么这将更加明显。 - SiegeX
2
请参考@Ascherer的答案,以获得更加优雅的解决方案。 - user918938
3
优雅很好,但我更想正确。:p - Amadan
显示剩余5条评论

83

当您想要处理一系列字段时,awk 没有直接的方法来实现这一点。我建议使用 cut 代替:

cut -d' ' -f 9- ./infile

编辑

由于默认为制表符,因此添加了空格字段分隔符。感谢 Glenn 指出这一点。


18
关于cut命令的一个特点是它使用特定的分隔符(默认为制表符),而awk命令使用“空格符号”。在cut命令中,两个连续的制表符表示一个空字段。 - glenn jackman
1
正如@glennjackman所指出的,awk的分隔符是“空格”(任意数量)。因此,将cut分隔符设置为单个空格也无法匹配行为。不幸的是,循环是最好的选择,看起来是这样。 - poncha
这个命令不能正常工作。尝试使用命令 find . | xargs ls -l | cut -d' ' -f 9-。由于某种原因,双空格也被计算在内。例如:lrwxrwxrwx 1 me me 21 Dec 12 00:00 ./file_a lrwxrwxrwx 1 me me 64 Dec 6 00:06 ./file_b 将导致 ./file_a 00:06 ./file_b 的结果。 - user2210287
@MarcoPashkov,请详细说明“这个不正常工作”,特别是考虑到您在管道中使用完全相同的代码。顺便说一下,您应该永远不要尝试解析ls的输出 - SiegeX
1
这里不适合使用 cut。例如,如果您的输入为 "foo bar"(单个空格)一行,而为另一行是 "foo ___ bar"(即多个空格,但 SO 太聪明了,无法显示),cut 会以不同的方式处理它们。 - UKMonkey
@UKMonkey,这个问题很容易解决,只需在管道符号前加上 tr -s ‘ ‘ 命令,再将其传递给 cut 命令即可。这样可以确保在切割时没有重复的空格。 - SiegeX

57
awk '{print substr($0, index($0,$9))}'

编辑:请注意,如果第九个字段之前的任何字段包含与第九个字段相同的值,则此方法无效。


10
如果第九个字段之前的任何一个字段包含与第九个字段相同的值,则此方法将无效。 - Amadan
7
可能是因为经典的一句话“希望你的文件没有这个问题”,在软件工程中完全是一个禁忌,因为声明“我们不打算浪费时间对例如负值之类的输入进行错误检查,因为‘我们希望用户足够聪明,不会尝试这些操作,从而使我们的工具崩溃’”是不可接受的。哈哈哈!总是很喜欢听到这个!(我喜欢幽默感)好吧,既然白痴确实存在,开发者的责任就是让他的东西防白痴!而不是“寄希望于人们的善意”。这种态度更适合哲学家,而不是软件工程师...LOL - syntaxerror
4
我并没有说不要检查错误,但如果你知道不会遇到问题,那么这个解决方案就可以了,就像我之前说的一样。但是感谢@syntaxerror 不必要的点踩。这个解决方案对一些人是有效的,正如目前的19个赞所显示的那样,但如果不行,那就不要用它作为你的解决方案。有很多方法可以解决问题。 - Ascherer
3
如果你在日常工作中使用awk命令行,那么这绝对是你想要的解决方案。这不是很明显吗?在这种情况下,错误检查等并不真的重要,因为你正在输入它并且可以在按下回车键之前捕获这些问题(就我个人而言,我认为awk不应该用于其他任何东西,这就是为什么我们有了perl、python、tcl和大约100多种其他更好、更快、更少烦人的脚本语言!)当然,也许我太高估我的软件开发同行们了,他们可能确实需要在即兴打字时进行错误检查(??) - osirisgothra
1
虽然它在答案下面,但我还是添加了它。 - Ascherer
显示剩余2条评论

13
sed -re 's,\s+, ,g' | cut -d ' ' -f 9-

将所有变宽的空格替换为单个空格,而不必处理它们。然后使用感兴趣的字段进行简单的cut命令。

这段话中没有使用awk,因此与其他答案/评论无关,但似乎很适合。


1
请更加详细地回答问题,否则将其作为评论发布。 - Alper Turan
这非常适合使用 ps faux |。永远不要害怕承认工具XYZ并不是最合适的选择。 - Kevin
@Kevin 更理想的是 ps faux | perl -pe 's/^(\H*\h*){8}//'。请查看我的回答。 - Robin A. Meade
因为问题是关于如何在awk中实现,而不是sed、perl、ruby、java、python或bash,所以我会将其downvote。 - Marco

11

通常情况下,Perl 取代了 awk/sed/grep 等工具,并且更加通用(而且只是个更好的万能刀)。

perl -lane 'print "@F[8..$#F]"'

当然,TIMTOWTDI适用于这种情况。


你需要添加命令行选项“-l”,或者在打印语句中添加“\n”。 - glenn jackman
@glenn jackman:可能吧。如果是另一条消息的一部分,或者被分配给一个变量等,则不需要。至于“更好”的问题,Perl在小型程序中确实看起来更好。不过要承认,在大型程序中可能会显得非常凌乱。 - bobbogo
别误会,我喜欢 Perl。但我也很爱 awk。 - glenn jackman
我的嵌入式设备没有Perl,但它有awk。 - Sepero
2
因为问题要求使用 awk 而不是 perl、ruby、java、python 或 bash,所以会被投反对票。 - Tom Harrison

5
awk -v m="\x01" -v N="3" '{$N=m$N ;print substr($0, index($0,m)+1)}'

这个功能可以截取给定字段号N之前的内容,并打印剩余的所有行,包括字段号N,并保持原来的间距(不重新格式化)。如果该字段的字符串也出现在行中的其他位置,则无关紧要,这是Ascherer答案存在的问题。

定义一个函数:

fromField () { 
awk -v m="\x01" -v N="$1" '{$N=m$N; print substr($0,index($0,m)+1)}'
}

并且像这样使用:

$ echo "  bat   bi       iru   lau bost   " | fromField 3
iru   lau bost   
$ echo "  bat   bi       iru   lau bost   " | fromField 2
bi       iru   lau bost   

输出保留所有内容,包括尾随空格,对于N=0,它会返回整行,对于n>NF则返回空字符串。


这是一个好主意。在当前的Mac上使用典型的gawk时,它并不完全有效,因为$0会崩溃。解决方法是将变量设置为第一步的$0,例如:'{s=$0; ... print substr(s,index(s,m)+1}' - joelparkerhenderson
这肯定会重新格式化该行,因为$N=m$N正在更改字段的值,这会导致awk使用OFS重建$0并替换所有FS。鉴于该脚本,我无法想象您如何获得所显示的输出。 - Ed Morton

1
这是一个关于ls -l输出的例子:

-rwxr-----@ 1 ricky.john  1493847943   5610048 Apr 16 14:09 00-Welcome.mp4
-rwxr-----@ 1 ricky.john  1493847943  27862521 Apr 16 14:09 01-Hello World.mp4
-rwxr-----@ 1 ricky.john  1493847943  21262056 Apr 16 14:09 02-Typical Go Directory Structure.mp4
-rwxr-----@ 1 ricky.john  1493847943  10627144 Apr 16 14:09 03-Where to Get Help.mp4

我的解决方案是在$9之后打印任何内容,使用awk '{print substr($0, 61, 50)}'

0

要显示前3个字段并打印剩余的字段,您可以使用:

awk '{s = ""; for (i=4; i<= NF; i++) s= s $i : "; print $1 $2 $3 s}' filename

其中 $1 $2 $3 是前三个字段。


0
function print_fields(field_num1, field_num2){
    input_line = $0

    j = 1;
    for (i=field_num1; i <= field_num2; i++){
        $(j++) = $(i);

    }
    NF = field_num2 - field_num1 + 1;
    print $0

    $0 = input_line
}

0
通常希望将剩余列“未修改”传递。也就是说,不要折叠连续的空格。
想象一下处理 `ls -l` 或 `ps faux`(不建议,只是给出最后一列可能包含空格序列的示例)的输出情况。我们希望保留剩余列中任何连续的空格,使得名为 `my file.txt` 的文件不会变成 `my file.txt`。
使用 `awk` 保留行中其余部分的空格非常困难。即使使用所建议的改进,也无法解决这一问题。更适合这项任务的工具是 `sed` 或 `perl`。 sed
echo '1 2 3 4 5 6 7 8 9   10' | sed -E 's/^([^ \t]*[ \t]*){8}//'

结果:

9   10

-E选项启用现代ERE正则表达式语法。这样我就不必反斜杠转义括号和大括号了。

{8}是一个量词,表示精确匹配前面的项目8次。

sed s命令将8个空格分隔的单词替换为一个空字符串。行的其余部分保持不变。

perl

Perl regex支持\h转义水平空白符。

echo '1 2 3 4 5 6 7 8 9   10' | perl -pe 's/^(\H*\h*){8}//'

结果:

9   10

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接