我有一个看起来像这样的日志文件,
Another thousand lines above this
I 10/03/15 12:04AM 42 [Important] 4th to last
I 10/03/15 04:31AM 42 (534642712) [1974,2106,258605080,0,0,32817,30711]
I 10/03/15 04:33AM 42 (2966927) [91,0,2966927,0,0,291,291]
I 10/03/15 04:52AM 42 (3026559) [93,0,3026559,0,0,314,314]
I 10/03/15 04:55AM 42 (3065494) [94,0,3065494,0,0,301,301]
I 10/03/15 05:04AM 42 [Important] 3rd to last
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42 (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence
整个文件中唯一保持不变的模式是[Important]
,其他所有内容都发生变化,包括每次出现之间的行数[Important]
我试图获取该文件的末尾,忽略最后一次出现的位置并找到倒数第二个,然后将文件的剩余内容提取到另一个文件中。
这是我一直在尝试的,但无法用 tac 挑出第二个到最后一个出现的情况。我正在尝试什么..
<logfile tac | sed '/Important/q' | tac > output_file
这就是输出应该的样子..
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42 (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence
答案1
找到所有带有“Important”的行,选择最后两行,获取行号,打印范围:
sed -n `grep -n Important log | tail -n 2 | cut -d : -f 1 | tr '\n' ',' | sed -e 's#,$#p#'` log
输出如预期:
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42 (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence
作为脚本:
#!/bin/bash
lines=`grep -n Important log | tail -n 2 | cut -d : -f 1`
range=`echo "${lines}" | tr '\n' ',' | sed -e 's#,$#p#'`
sed -n "${range}" log
答案2
$ awk '/Important/{pen=s; s=$0;next} s{s=s"\n"$0} END{print pen "\n" s}' logfile
I 10/04/15 12:04AM 42 [Important] 2nd to last occurence
I 10/04/15 04:31AM 42 (7,30711]55
I 10/04/15 04:33AM 42 dfsadfs,0,0,291,291]
I 10/04/15 04:52AM 42 (30,0,314,314]
I 10/04/15 04:55AM 42 (30,301]
I 10/04/15 05:04AM 42 [Important] - last occurence
怎么运行的
awk 隐式循环输入文件中的所有行。每次出现 后Important
,我们将这些行保存在变量中s
。当我们到达其中的新行时Important
,旧的重要行集将被转移到变量中pen
,并且我们开始将新行保存在 中s
。
pen
有倒数第二(倒数第二)Important
部分。 s
有最终(最后)Important
部分。最后,我们打印pen
和s
。
更详细地说:
/Important/{pen=s; s=$0;next}
如果该行包含
Important
,则将变量的内容移至s
,pen
保存当前行s
。然后,跳过其余命令并跳转到下一行。s{s=s"\n"$0}
如果我们到达这里,那么当前行不包含
Important
.如果
s
已设置为一个值,则将当前行附加到该值。END{print pen "\n" s}
到达文件末尾后,打印
pen
和s
。
答案3
如果ed
是一个选项:
ed -s file <<EOF
1
?Important
?
;w output_file
Q
EOF
答案4
如果 sed 可以缓冲整个文件(如果你使用的是 GNU/任何东西,它可以),
(最后编辑:我在这里修复了多个 Brainos)
sed -En 'H;$!d
g;s/.*[\n](.*Important.*\n.*Important[^\n]*).*/\1/p
'
缓冲H;$!d
(“保留”)每一行,直到\n
文件末尾。后面的内容$!d
仅在最后一行缓冲后运行。g
g
ets 缓冲区。
要理解正则表达式,请记住正则表达式是最左最长的。前导 .* 查找最后一场比赛以下内容。由于H
无条件地将 a 附加\n
到前面,.*\n
因此匹配两个“重要”之前的每个完整行,该“重要”之间至少有一个换行符,后面是任何下一行之前的所有内容。
如果没有两行重要的行,则不会打印任何内容。
当您发现不需要的线条时,逐渐丢弃它们至少在美学上会更好
sed -En 'H
/Important/ {x; s/.*[\n](.*Important.*\n.*Important[^\n]*)/\1/; H}
$ {g; s/.*[\n](.*Important.*\n.*Important[^\n]*).*/\1/p }
'
匹配/Important/
ex
更改模式和保持缓冲区,仅保留最后一个感兴趣的块,并将结果放回保持缓冲区。
我将 放在[\n]
括号中只是为了突出显示它并在视觉上将其与尾随的非换行符类相匹配,当然可以在没有括号的情况下编写单字符类。