awk指定分隔符时,没有匹配到分隔符的行会怎样?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了awk指定分隔符时,没有匹配到分隔符的行会怎样?相关的知识,希望对你有一定的参考价值。

awk使用的一些技巧记录

awk指定分隔符时,没有匹配到分隔符的行会怎样?

一个经典题目:从下面的文件里获取HH:MM

[root@Almalinux-VM1 awk]# cat HM
183.250.220.178|-l[20/jul/2017:10:35:14 +0800]|POST /audiosearch/search
HTTP/1.1|200|54|-lDalvik/1.6.0(linux;U;android 4,4,4;Konka Android TV 638
Build/KTU84P)|-l-[5.069|5.001,0.005|www.kuyun.com|8771|172.21.19.67:8084,172.21.19.66:8084]
183.250.220.178|-l[20/jul/2017:10:35:14 +0800]|POST /audiosearch/search
HTTP/1.1|200|54|-lDalvik/1.6.0(linux;U;Android 4,4,4;Konka Android TV 638
Build/KTU84P)|-l-[5.069|5.001,0.005|www.kuyun.com|8771|172.21.19.67:8084,172.21.19.66:8084]

我最早的思路:使用正则匹配[20/jul/2017],再用管道再执行一次awk.见下

[root@Almalinux-VM1 awk]# awk BEGIN FS="|" $2 ~/[0-9]\\/[a-z]3/ print $2 HM
-l[20/jul/2017:10:35:14 +0800]
-l[20/jul/2017:10:35:14 +0800]

[root@Almalinux-VM1 awk]# awk BEGIN FS="|" $2 ~/[0-9]\\/[a-z]3/ print $2 HM |awk BEGIN FS=":" print $2,":",$3
10 : 35
10 : 35

可以看到实现了要求.

看到别人写的标准答案

[root@Almalinux-VM1 awk]# awk BEGIN FS="2017:|:14"  print $2 HM
10:35


10:35

大吃一惊,原来可以这样写.引出一个问题,文件有的行没有2017:|:14,那执行他们的时候是怎么执行?

[root@Almalinux-VM1 awk]# awk BEGIN FS="2017:|:14"  print $1 HM
183.250.220.178|-l[20/jul/
HTTP/1.1|200|54|-lDalvik/1.6.0(linux;U;Android 4,4,4;Konka Android TV 638
Build/KTU84P)|-l-[5.069|5.001,0.005|www.kuyun.com|8771|172.21.19.67:8084,172.21.19.66:8084]
183.250.220.178|-l[20/jul/
HTTP/1.1|200|54|-lDalvik/1.6.0(linux;U;Android 4,4,4;Konka Android TV 638
Build/KTU84P)|-l-[5.069|5.001,0.005|www.kuyun.com|8771|172.21.19.67:8084,172.21.19.66:8084]
[root@Almalinux-VM1 awk]# awk BEGIN FS="2017:|:14" print $2 HM
10:35


10:35


[root@Almalinux-VM1 awk]# awk BEGIN FS="2017:|:14" print $3 HM
+0800]|POST /audiosearch/search


+0800]|POST /audiosearch/search
对比以上的执行过程可以发现,当指定FS,没有匹配到指定的FS的行,整行默认为$1.

再来测试下

[root@Almalinux-VM1 awk]# awk BEGIN FS="MP3" print items.txt 
101,HD Camcorder,Video,210,10
102,Refrigerator,Appliance,850,2
103,MP3 Player,Audio,270,15
104,Tennis Racket,Sports,190,20
105,Laser Printer,Office,475,5
[root@Almalinux-VM1 awk]# awk BEGIN FS="MP3" print $1 items.txt
101,HD Camcorder,Video,210,10
102,Refrigerator,Appliance,850,2
103,
104,Tennis Racket,Sports,190,20
105,Laser Printer,Office,475,5
[root@Almalinux-VM1 awk]# awk BEGIN FS="MP3" print $2 items.txt


Player,Audio,270,15

Bash awk 打印匹配的分隔符

【中文标题】Bash awk 打印匹配的分隔符【英文标题】:Bash awk print matched delimiter 【发布时间】:2018-02-19 14:30:09 【问题描述】:

有没有办法用 awk 打印当前匹配的分隔符?

例如:

awk -F '["RESTART" | "FAILURE" | "WARNING" | [:blank:]]2' 'FNR > 4  for (i=1; i<=NF; i++) print $i;' file

示例输入

XX      XXXX   RESTART 6666  XX X
XXXX    XXXX   WARNING 8888  YYY YYY
XXX     XXXX   INFORM  7777  XXXX XX

示例输出(必须)

XX
XXXX
RESTART
6666
XX X
XXXX
XXXX
WARNING
8888
YYY YYY
XXX
XXXX
INFORM
7777
XXXX XX

示例输出(现在)

XX
XXXX
6666
XX X
XXXX
XXXX
8888
YYY YYY
XXX
XXXX
INFORM
7777
XXXX XX

我使用超过 2 个空格作为列分隔符,但在某些情况下 (RESTART 6666) 或 (WARNING 8888) 两列没有被两个空格分隔,这就是为什么我必须使用内容 ( RESTART, WARNING) 作为分隔符,但是如果我使用内容作为分隔符,它不会被显示,所以我想显示/打印使用的分隔符(如果它是内容而不是空格)

主要问题是区分一个空格用作列分隔符和一个空格用作一列中的单词分隔符。我不能影响我必须处理的文件。

【问题讨论】:

你能用实际的分隔符和实际的文件内容发布问题吗?这些信息不充分 添加了更多提示 看起来很混乱。发布输入片段和期望的结果 您的数据xxx 之间可以有一个空格吗? @batMan 是的,查看示例输出 【参考方案1】:

awk:

awk 'gsub(/  +|\t/,"\n") print' file | awk '/RESTART|WARNING|FAILURE/gsub(/ /,"\n")  print'

gsub(/ +|\t/,"\n"):用换行符\n替换“2个或更多空格或\t”。 这会将我们的文件转换为多行,其中每行可以包含多个单词,仅由单个空格分隔。

/RESTART|WARNING|FAILURE/gsub(/ /,"\n") :如果行包含这 3 个单词之一,则将空格 替换为 \n

您也可以使用 sed

sed "s/\s\s\+/\n/g; s/\(RESTART\|WARNING\|FAILURE\) /\1\n/g"  file

对于较旧的 sed 版本(主要在 MAC 中):可能不支持 +,因此请修改为 *

sed "s/\s\s\s*/\n/g; s/\(RESTART\|WARNING\|FAILURE\) /\1\n/g"  file

s/\s\s\+/\n/g :将 2 个或多个空格替换为单个 \ns/\(RESTART\|WARNING\|FAILURE\) /\1\n/g :将空格替换为 \n 在您之后 三个例外

输入:

line one      hello hello   RESTART 6666  XX X
line two    hello hello   WARNING 8888  YYY YYY
line three  hello hello      INFORM  7777  XXXX XX

输出:

line one
hello hello
RESTART
6666
XX X
line two
hello hello
WARNING
8888
YYY YYY
line three
hello hello
INFORM
7777
XXXX XX

【讨论】:

你确定它会给出那个输出吗?我在控制台中打印了完全没有拆分的完整文件 您可能使用的是不支持 + 的旧 sed 版本(您在 mac 上吗?).. 尝试我更新的答案... 带有 bash 2 的 solaris 5.8。 这很有趣,我正要评论“如果您认为 MAC 上的 sed 已经过时,请等到看到 Solaris”:-)。是的,Solaris 上的默认 sed 不支持 \s 用于空格,也不支持 \| 或任何其他 ERE 元字符。 idk 如果 /usr/xpg4/bin 中有更新的 sed 但看看... @Sirion:你能试试这个awk 'gsub("( *)|\t","\n") print' file | awk '/RESTART/ || /WARNING/ || /FAILURE/gsub(" ","\n") print'。我也算过\t。如果您没有标签或不想计入标签,则可以将其从 gsub 中删除。让我回答一下,因为有些空格被截断了。【参考方案2】:

这是一种适用于任何 awk 的固定宽度字段方法(当然,Solaris 上旧的损坏的 awk /bin/awk 除外,您应该使用 /usr/xpg4/bin/awk 代替):

$ cat tst.awk

    # identify the fields:
    nf = 0
    f[++nf] = substr($0,1,8)
    f[++nf] = substr($0,9,7)
    f[++nf] = substr($0,16,8)
    f[++nf] = substr($0,24,6)
    f[++nf] = substr($0,30)

    # remove leading/trailing white space from each field:
    for (i in f) 
        sub(/^[[:space:]]+/,"",f[i])
        sub(/[[:space:]]+$/,"",f[i])
    

    # print the fields:
    for (i=1; i<=nf; i++) 
        print NR, i, "<" f[i] ">"
    
    print "---"

.

$ awk -f tst.awk file
1 1 <XX>
1 2 <XXXX>
1 3 <RESTART>
1 4 <6666>
1 5 <XX X>
---
2 1 <XXXX>
2 2 <XXXX>
2 3 <WARNING>
2 4 <8888>
2 5 <YYY YYY>
---
3 1 <XXX>
3 2 <XXXX>
3 3 <INFORM>
3 4 <7777>
3 5 <XXXX XX>
---

如果您在 Solaris 上使用了 nawk,那么您必须将 [[:space:]] 替换为 [ \t],因为它早于 POSIX 字符类,但不要使用 nawk,而是使用 /usr/xpg4/bin/awk。

如果这种方法适合您,可以修改为使用循环而不是 5 次显式 substr() 调用。

【讨论】:

【参考方案3】:

也许您可以将 GNU awk 的 splitseps 一起使用。 https://www.gnu.org/software/gawk/manual/html_node/String-Functions.html 告诉:

split(string, array [, fieldsep [, seps ] ])

seps 是一个 gawk 扩展,其中 seps[i] 是 array[i] 和 array[i+1] 之间的分隔符字符串。

【讨论】:

以上是关于awk指定分隔符时,没有匹配到分隔符的行会怎样?的主要内容,如果未能解决你的问题,请参考以下文章

Linux -awk

7.5

2018-1-17 6周3次课

awk命令

shell中最强大,实用的命令awk

AWK的使用