使用OPEN STEP 4.2 OS ...我目前正在使用以下sed
命令:
sed -n '1,/141.299.99.1/p' TESTFILE | tail -3
此命令将在IP为141.299.99.1的文件中找到一个实例,并且在它之前还包含3行,这一切都很好,不过我也想找到IP的所有实例以及它之前的3行不只是第一个
使用OPEN STEP 4.2 OS ...我目前正在使用以下sed
命令:
sed -n '1,/141.299.99.1/p' TESTFILE | tail -3
此命令将在IP为141.299.99.1的文件中找到一个实例,并且在它之前还包含3行,这一切都很好,不过我也想找到IP的所有实例以及它之前的3行不只是第一个
Answers:
这是grep -B3
基于此GNU sed示例(但希望与POSIX兼容-并带有@StéphaneChazelas的致谢),使用sed移动窗口进行仿真的尝试:
sed -e '1h;2,4{;H;g;}' -e '1,3d' -e '/141\.299\.99\.1/P' -e '$!N;D' file
前两个表达式为多行模式缓冲区加注,并允许它处理第一次匹配之前少于3行的先前上下文的边缘情况。中间(正则表达式匹配)表达式在窗口顶部打印一行,直到所需的匹配文本通过模式缓冲区起伏。最终$!N;D
将窗口滚动一行,除非到达输入末尾。
-e
不是特定于GNU的。要成为POSIX /便携式产品,您确实需要它,因为之后没有任何东西}
(并且您需要;
在它之前)。
-e '1h;2,4{H;g;}' -e '1,3d'
?我没有要测试的非GNU系统(而且GNU sed --posix
开关似乎不在乎)。
sed
传家宝工具箱(它是传统Unix sed的后代)测试不同的实现。该POSIX / Unix的规格为sed
是pubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
与sed
您可以做一个滑动窗口。
sed '1N;$!N;/141.299.99.1/P;D'
做到了。但是要当心- 即使被引用,它bash
的疯狂行为!
也会扩大!!!从命令历史记录中输入命令字符串可能会使它有些疯狂。set +H;
如果是这种情况,请在命令前加上前缀。然后,为了重新启用它(但为什么???)做set -H
之后。
那当然,只会如果应用被使用bash
-尽管我不相信你。我可以肯定地说,您正在使用csh
- (这恰好是其行为bash
与历史扩展类似的shell,但可能不是c shell接受的极端)。所以,大概一\!
应工作。我希望。
都是可移植的代码:POSIX这样描述了它的三个运算符:(尽管值得注意的是,我仅确认此描述早在2001年就已存在)
[2addr]N
\n
使用嵌入的\n
ewline将附加的材料与原始材料分开,将 输入的下一行(减去其终止ewline)附加到图案空间。请注意,当前行号会更改。
[2addr]P
将模式空间(直到第一个\n
ewline)写入标准输出。
[2addr]D
通过第一个\n
ewline 删除模式空间的初始段,然后开始下一个循环。
因此,在第一行上,您需要在模式空间中添加一条额外的行,因此如下所示:
^line 1s contents\nline 2s contents$
然后在第一行及其后的每一行(最后一行除外)上,将另一行添加到模式空间。所以看起来像这样:
^line 1\nline 2\nline 3$
如果在您的ip地址中找到P
了第一个换行符,那么请在这里输入第1行。在每个周期的末尾,您都要选择D
相同的内容,然后从剩下的内容重新开始。因此,下一个周期如下所示:
^line 2\nline 3\nline 4$
...等等。如果要在这三个中的任何一个上找到您的IP,则每次都会打印出来。因此,您始终仅领先三行。
这是一个简单的例子。我将为每个以零结尾的数字打印一个三行缓冲区:
seq 10 52 | sed '1N;$!N;/0\(\n\|$\)/P;D'
10
18
19
20
28
29
30
38
39
40
48
49
50
那比您的情况要复杂一点,因为我不得不0\n
换行或0$
模式空间的以更接近于您的问题-但是它们之间的细微差别在于这需要锚点-这样做可能有点困难模式空间不断变化。
我使用10和52的奇数情况来说明,只要锚点是柔性的,输出也是。完全可移植,我可以通过依靠算法来实现相同的结果并执行以下操作:
seq 10 52 | sed '1N;$!N;/[90]\n/P;D'
并扩大搜索范围,同时限制我的窗口-从0到9和0,从3行增加到2。
无论如何,你明白了。
sed '...' $filename
。顺便说一句-我从您自己的搜索字符串中删除了句点,但实际上不是模式中的句点-那些代表任何单个字符。您可能应该oct\.oct\.oct\.oct
逃脱它们,以便它们仅匹配句点。
您可以在shell本身中实现与其他non-grep答案相同的基本方法(这是假设一个相对较新的shell支持=~
):
while IFS= read -r line; do
[[ $line =~ 141.299.99.1 ]] && printf "%s\n%s\n%s\n%s\n" $a $b $c $line;
a=$b; b=$c; c=$line;
done < file
或者,您可以将整个文件插入到一个数组中:
perl -e '@F=<>;
for($i=0;$i<=$#F;$i++){
print $F[$i-3],$F[$i-2],$F[$i-1],$F[$i] if $F[$i]=~/141.299.99.1/
}' file
perl
,可以使用ack
。
awk '/141.299.99.1/{for(i=1;i<=x;)print a[i++];print} {for(i=1;i<x;i++)
a[i]=a[i+1];a[x]=$0;}' x=3 filename
在此awk
解决方案中,使用的数组将始终在当前模式之前包含3行。因此,当匹配模式时,将打印数组内容以及当前模式。
测试中
-bash-3.2$ cat filename
10.0.0.1
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.5
10.0.0.6
10.0.0.7
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.11
10.0.0.12
10.0.0.13
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
10.0.0.17
10.0.0.18
10.0.0.19
执行命令后,输出为
10.0.0.2
10.0.0.3
10.0.0.4
141.299.99.1
10.0.0.8
10.0.0.9
10.0.0.10
141.299.99.1
10.0.0.14
10.0.0.15
10.0.0.16
141.299.99.1
在大多数情况下,/141.299.99.1/
还将匹配(例如)141a299q99+1
或141029969951
因为.
在正则表达式中可以表示任何字符。
使用/141[.]299[.]99[.]1/
更安全,你可以在开始添加额外的背景下,整个正则表达式的结束,以确保它不匹配3141.
,.12
,.104
,等。