在awk中使用多个分隔符

284

我有一个包含以下内容的文件:

/logs/tc0001/tomcat/tomcat7.1/conf/catalina.properties:app.env.server.name = demo.example.com
/logs/tc0001/tomcat/tomcat7.2/conf/catalina.properties:app.env.server.name = quest.example.com
/logs/tc0001/tomcat/tomcat7.5/conf/catalina.properties:app.env.server.name = www.example.com

在上面的输出中,我想提取3个字段(第2个、第4个和最后一个*.example.com)。我得到了以下输出:
cat file | awk -F'/' '{print $3 "\t" $5}'
tc0001   tomcat7.1
tc0001   tomcat7.2
tc0001   tomcat7.5

如何提取等号后面的域名作为最后一个字段?如何使用多个分隔符来提取字段?


2
为了回答我的问题,即同样但不同的问题,“awk”在字段为空时会吞咽它们,这会破坏字段编号。我将“-F”从“ ”更改为“[ ]”,然后“awk”就不再吞咽空字段了。 - Adam
8个回答

460
分隔符可以是一个正则表达式。
awk -F'[/=]' '{print $3 "\t" $5 "\t" $8}' file

输出:

tc0001   tomcat7.1    demo.example.com  
tc0001   tomcat7.2    quest.example.com  
tc0001   tomcat7.5    www.example.com

50
当然,cat 进程并非必需的:awk '...' file. 此外,使用输出字段分隔符会更加整洁:awk -F'[/=]' -v OFS="\t" '{print $3, $5, $8}' - glenn jackman
23
Awk的分隔符可以是正则表达式...这让我很开心! - das.cyklone
5
awk命令也可以有多个分隔符,例如:awk -F 'this|that|[=/]' '......'(有用的是用单词/字符串分隔事物)。请注意,此方法会保留两个分隔符之间字段中的空格。添加|[ \t]+也可能有用,但会让事情变得棘手……因为在“this”之前和之后通常会有空格,这将使额外的2个空字段出现在空格和“this”之间。 - Olivier Dulac
我已经在两个不同的发行版上尝试过了,但是得到了相同的结果: 我想从netstat -ntpl中获取端口号。 "netstat -ntpl |sed 's/:/ /' |awk '{print $5}' "可以工作,但是不需要双重管道。 这个命令可以工作,但是我没有预料到第17个字段上会有数据: "netstat -ntpl |awk -F" |:" '{print $17}'" - louigi600
3
是的...这让我得到了我想要的:awk -F"[ :]+" '//postmaster *$/ {print $5}' - louigi600
显示剩余5条评论

75

好消息!awk字段分隔符可以是正则表达式。你只需要使用-F"<separator1>|<separator2>|..."

awk -F"/|=" -vOFS='\t' '{print $3, $5, $NF}' file

返回:

tc0001  tomcat7.1  demo.example.com
tc0001  tomcat7.2  quest.example.com
tc0001  tomcat7.5  www.example.com

这里是:
  • -F"/|=" 将输入字段分隔符设置为斜杠或等号。

  • -vOFS='\t' 使用-v标志设置变量。 OFS 是输出字段分隔符的默认变量,它被设置为制表符字符。该标志是必需的,因为没有内置的OFS标志。

  • {print $3, $5, $NF} 根据输入字段分隔符打印第三个、第五个和最后一个字段。


请看另一个例子:
$ cat file
hello#how_are_you
i#am_very#well_thank#you

这个文件有两个字段分隔符,#_。如果我们想要打印第二个字段但不管是哪种分隔符,那么让两个都成为分隔符吧!

$ awk -F"#|_" '{print $2}' file
how
am

文件编号如下:

hello#how_are_you           i#am_very#well_thank#you
^^^^^ ^^^ ^^^ ^^^           ^ ^^ ^^^^ ^^^^ ^^^^^ ^^^
  1    2   3   4            1  2   3    4    5    6

10

另一种方法是使用-F选项,但要传递正则表达式来打印左括号(和右括号)之间的文本。

文件内容:

528(smbw)
529(smbt)
530(smbn)
10115(smbs)

命令:

awk -F"[()]" '{print $2}' filename

结果:

smbw
smbt
smbn
smbs
使用awk仅打印[]之间的文本:
使用 awk -F'[][]',但 awk -F'[[]]' 无效。 http://stanlo45.blogspot.com/2020/06/awk-multiple-field-separators.html

3
你的回答出现在删除队列中,因为99%的情况下,声誉值为1的用户链接到自己的博客通常是垃圾信息。但你是例外。过去10年的内容是一座金矿,希望你有计划让它永存。 - Eric Leschinski

6
如果您的空格是一致的,可以使用它作为分隔符,而且不必直接插入\t,您可以设置输出分隔符,它会自动包含在内。
< file awk -v OFS='\t' -v FS='[/ ]' '{print $3, $5, $NF}'

你可以通过在代码块的右侧(与数据文件列表的位置相同)放置FS=..OFS=…来跳过-v 部分。尽管它们在 RHS 上,但它们的赋值仍将及时完成第一行数据,因为你没有需要对它们进行额外处理的BEGIN { }块。 - RARE Kpop Manifesto

5

对于任意数字2到5或字母a或#或空格作为字段分隔符,分隔字符必须至少重复2次,但不超过6次,例如:

awk -F'[2-5a# ]{2,6}' ...

我确信有使用()和参数的变体存在。


3

Perl一行脚本:

perl -F'/[\/=]/' -lane 'print "$F[2]\t$F[4]\t$F[7]"' file

这些命令行选项如下:
  • -n 循环读入文件的每一行,将该行存储在 $_ 变量中,不会自动打印每一行

  • -l 在处理前删除换行符,并在处理后添加回来

  • -a 自动分割模式 - Perl 将自动将输入行拆分为 @F 数组。默认情况下按空格进行拆分

  • -F 自动分割修饰符,在此示例中按 /= 进行拆分

  • -e 执行 Perl 代码

Perl 与 awk 密切相关,但是 @F 自动拆分数组从索引 $F[0] 开始,而 awk 字段从 $1 开始。


如果没有设置-a标志,perl是否允许最终用户创建名为@F的数组? - RARE Kpop Manifesto
这是因为@F数组并没有特殊之处。 - Chris Koknat
啊,谢谢。我只是觉得perl可能会让它更一致- 因为子程序的变量参数会自动拆分成@_,那么将主输入行$_也自动拆分成依赖范围的@_会更直观,而不是用@F - RARE Kpop Manifesto

0

使用Raku(前身为Perl_6)

raku -ne '.split(/ <[/=]> /).[2,4,7].put;'

示例输入:

/logs/tc0001/tomcat/tomcat7.1/conf/catalina.properties:app.env.server.name = demo.example.com
/logs/tc0001/tomcat/tomcat7.2/conf/catalina.properties:app.env.server.name = quest.example.com
/logs/tc0001/tomcat/tomcat7.5/conf/catalina.properties:app.env.server.name = www.example.com

样例输出:

tc0001 tomcat7.1  demo.example.com
tc0001 tomcat7.2  quest.example.com
tc0001 tomcat7.5  www.example.com

上面是一个使用Raku编写的解决方案,它是Perl编程语言家族的成员之一。简而言之,输入使用-ne(逐行读取,非自动打印)命令行标志进行逐行读取。行通过正则表达式/=进行split,该正则表达式由使用<[]>运算符创建的自定义字符类组成。然后将元素[2,4,7] put以给出上面的结果。
当然,上述是一个“裸骨”实现,而且Raku是Perl语言家族的一种语言,TMTOWTDI适用于此。因此,行可以使用由|“OR”运算符分隔的文字字符进行split。元素编号(在Perl和Raku中都是从零开始索引)可以通过向split例程添加:skip-empty副词来缩紧。可以使用map从每个元素中去除空格,并使用\t制表符join所需的元素(现在是[1,3,6]),从而得到以下结果:
raku -ne '.split(/ "/" | "=" /, :skip-empty).map(*.trim).[1,3,6].join("\t").put;' file
tc0001  tomcat7.1   demo.example.com
tc0001  tomcat7.2   quest.example.com
tc0001  tomcat7.5   www.example.com

https://raku.org


0

我看到黑板上已经有很多完美的答案了,但我仍想上传我的代码:

awk -F"/" '{print $3 " " $5 " " $7}' sam | sed 's/ cat.* =//g'


3
“print $3 " " $5 " " $7” 可以简化为 “print $3, $5, $7”。此外,我不认为使用awk并将其管道传输到sed有任何优势。一般情况下,只用awk就足够了,而其他答案也表明了这一点。 - fedorqui

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