我有一个数据文件,日期在第二列
# cat datafile
-;20210106;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
...
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
-;20210219;-;-;-;
-;20210221;-;-;*20210219*;
连字符“ -
”代表随机文本数据,点“ ...
”代表更多行数据,“*”代表同一列中的随机文本。我想要的只是基于第二列之间的20210112
数据20210219
。
我想避免,sed/grep
因为两者也会在其他列中 grep 类似的模式。
# sed -n '/20210112/,/20210219/p' datafile
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
...
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
-;20210219;-;-;-;
-;20210221;-;-;*20210219*;
它也会匹配其他不相关行中的一些其他文本。所以,我猜 AWK 是一个更好的候选者,但我注意到 awk 仅在第一个模式的第一个匹配到第二个模式的第一个匹配之间打印
# awk -F';' '$2 ~ /20210112/,$2 ~ /20210219/' datafile
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
...
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
然而,我想将所有行带到第二个模式的最后一场比赛。
期望的
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
...
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
-;20210219;-;-;-;
答案1
我就是这样做的:
BEGIN {FS = ";"}
$2 == 20210112 {capture = 1}
capture == 1 {buffer = buffer $0 "\n"}
$2 == 20210219 {printf ("%s", buffer); buffer = ""}
在看到第一个模式第一次出现后,它开始将行放入缓冲区。对于第二个模式的每次出现,它都会打印缓冲区,并再次将缓冲区重置为空字符串。
答案2
如果我们忽略该...
行并假设所有日期都按 2 美元的递增顺序排列(如示例所示),那么您所需要的只是:
$ awk -F';' '(20210112 <= $2) && ($2 <= 20210219)' file
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
-;20210219;-;-;-;
或者更有效,因为一旦超过范围它就会退出:
$ awk -F';' '20210112 <= $2{f=1} $2 > 20210219{exit} f' file
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
...
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
-;20210219;-;-;-;
上面还假设如果输入中不存在结束日期,则您希望从开始日期打印到文件末尾,如果开始日期不存在,则您希望从大于开始日期的第一个日期开始打印到结束日期等
答案3
确定何时最后的已经看到模式的出现通常需要记住之前的行,并且只有在确定模式不再出现时才输出它们将要可见。
为了避免手动执行此操作,您可以截断文件的开头,反转它,截断文件的新开头,然后再次反转:
awk -F\; '$2 == 20210112,0' | tac | awk -F\; '$2 == 20210219,0' | tac
答案4
您可以在 中完成awk
,您只需要稍微复杂一点的方法。请注意使用==
代替~
,这是为了避免匹配字段包含20210112
或20210219
作为子字符串的情况(例如20210219123
):
$ awk -F';' '$2==20210112{a=1}; $2==20210219 && a{b=1} a && b && $2!=20210219{exit}; a ' datafile
-;20210112;-;-;-;
-;20210112;-;-;-;
-;20210112;-;-;-;
...
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210217;-;-;-;
-;20210219;-;-;-;
-;20210219;-;-;-;
或者,更详细但更容易理解:
$ awk -F';' '{
if($2==20210112){ a=1 }
if($2==20210219 && a){ b=1 }
if(a && b && $2!=20210219){ exit };
if(a){ print }
}' datafile
或者,只需进行数值比较:
awk -F';' '$2>=20210112 && $2<=20210219' datafile
顺便说一句,sed
如果有必要,您仍然可以使用类似的东西,只需锚定模式,使其仅在第二个字段中匹配:
$ sed -n '/^[^;]*;20210112/,/^[^;]*;20210219/p' datafile
在这种情况下没有用,因为它将在第一个匹配处停止,但至少它不会在其他字段上匹配。