在AWK中,是否可以指定字段的"范围"?

36 awk

在AWK中,是否可以指定字段的"范围"?

例.给定一个制表符分隔的文件"foo",每行100个字段,我想只打印每行的字段32到57,并将结果保存在文件"bar"中.我现在应该做什么:

awk 'BEGIN{OFS="\t"}{print $32, $33, $34, $35, $36, $37, $38, $39, $40, $41, $42, $43, $44, $45, $46, $47, $48, $49, $50, $51, $52, $53, $54, $55, $56, $57}' foo > bar
Run Code Online (Sandbox Code Playgroud)

这样做的问题是输入和容易出错是很繁琐的.

是否有一些句法形式允许我以更简洁,更不容易出错的方式(如"$ 32 .. $ 57")说出同样的内容?

j.w*_*w.r 31

除了@Jerry 的awk 回答,还有其他选择:

使用cut(默认情况下假设制表符分隔符):

cut -f32-58 foo >bar
Run Code Online (Sandbox Code Playgroud)

使用perl:

perl -nle '@a=split;print join "\t", @a[31..57]' foo >bar
Run Code Online (Sandbox Code Playgroud)


Jer*_*fin 27

温和修订版:

BEGIN { s = 32; e = 57; }

      { for (i=s; i<=e; i++) printf("%s%s", $(i), i<e ? OFS : "\n"); }
Run Code Online (Sandbox Code Playgroud)


Ed *_*ton 6

您可以使用RE间隔在awk中执行此操作.例如,要打印此文件中记录的字段3-6:

$ cat file
1 2 3 4 5 6 7 8 9
a b c d e f g h i
Run Code Online (Sandbox Code Playgroud)

将会:

$ gawk 'BEGIN{f="([^ ]+ )"} {print gensub("("f"{2})("f"{4}).*","\\3","")}' file
3 4 5 6
c d e f
Run Code Online (Sandbox Code Playgroud)

我正在创建一个RE段f来表示每个字段加上它的后续字段分隔符(为方便起见),然后我在gensub中使用它来删除其中的2个(即前2个字段),记住接下来的4个用于参考稍后使用\ 3,然后删除它们之后的内容.对于要打印字段32-57(即前31个字段后面的26个字段)的制表符分隔文件,您将使用:

gawk 'BEGIN{f="([^\t]+\t)"} {print gensub("("f"{31})("f"{26}).*","\\3","")}' file
Run Code Online (Sandbox Code Playgroud)

以上使用GNU awk作为gensub()函数.使用其他awks,您可以使用sub()或match()和substr().

编辑:这是如何写一个函数来完成这项工作:

gawk '
function subflds(s,e,   f) {
   f="([^" FS "]+" FS ")"
   return gensub( "(" f "{" s-1 "})(" f "{" e-s+1 "}).*","\\3","")
}
{ print subflds(3,6) }
' file
3 4 5 6
c d e f
Run Code Online (Sandbox Code Playgroud)

只需根据需要设置FS.请注意,如果输入文件可以以空格开头和/或字段之间有多个空格,则需要对默认FS进行调整,并且只有在FS是单个字符时才能使用.

  • @Jotne 我希望如此,但我还没有对此进行测试。我这么说是因为它不仅避免了循环的迭代,而且通过不提及脚本中的任何字段,它关闭了字段分割,并且对于每个记录仅执行 `print gensub(,,s,,e,,)` 而不是等价于 `split(&lt;input&gt;,$0); for (i=s; i&lt;=e; i++) printf "%s%s", $i, (i&lt;e ? OFS : ORS)` (2认同)

小智 6

我迟到了,但这很快就到了,所以我会把它留在这里。在这种情况下,我通常只使用 gsub 和打印删除不需要的字段。快速而肮脏的示例,因为您知道您的文件由制表符分隔,您可以删除前 31 个字段:

awk '{gsub(/^(\w\t){31}/,"");print}'
Run Code Online (Sandbox Code Playgroud)

由于懒惰而删除 4 个字段的示例:

printf "a\tb\tc\td\te\tf\n" | awk '{gsub(/^(\w\t){4}/,"");print}'
Run Code Online (Sandbox Code Playgroud)

输出:

e   f
Run Code Online (Sandbox Code Playgroud)

与可怕的循环相比,这编写起来更短,更容易记住并且使用更少的 CPU 周期。