使用sed查找文件中的所有事件


15

使用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行不只是第一个


1
始终包括您的操作系统。解决方案通常取决于所使用的操作系统。您是否正在使用Unix,Linux,BSD,OSX等?哪个版本?
terdon

大点!使用Open Step 4.2版本已经很老了,所包含的Shell不包含下面答案中提到的许多功能。
戴尔

出于好奇-什么是OPEN STEP 4.2系统?它今天用于什么?
托尔比约恩Ravn的安徒生

(如果Perl是可用的,你真的可以做很多好吃的东西只是与)
托尔比约恩Ravn的安徒生

@ThorbjørnRavnAndersen也许就是这样:en.wikipedia.org/wiki/OpenStep
Barmar 2014年

Answers:


4

这是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 /便携式产品,您确实需要它,因为之后没有任何东西}(并且您需要;在它之前)。
斯特凡Chazelas

感谢@StéphaneChazelas-您是说要实现POSIX /便携式,第一组需要拆分/修改为-e '1h;2,4{H;g;}' -e '1,3d'?我没有要测试的非GNU系统(而且GNU sed --posix开关似乎不在乎)。
steeldriver

1
是的,在Linux上,您可以使用sed传家宝工具箱(它是传统Unix sed的后代)测试不同的实现。该POSIX / Unix的规格为sedpubs.opengroup.org/onlinepubs/9699919799/utilities/sed.html
斯特凡Chazelas

我在以下任一事件上均未找到事件:N; D':未找到事件。我在某处缺少语法吗?谢谢!!
Dale 2014年

抱歉,我刚刚意识到我的最新编辑在第一个-e表达式后省略了右引号。我已经更正了-您可以使用上面的表达式再试一次吗?
steeldriver

10

grep 会做得更好:

grep -B 3 141.299.99.1 TESTFILE

-B 3方式来打印每场比赛之前的三条线。这将--在每组线之间打印。要禁用它,请使用--no-group-separator

GNU和大多数BSD版本(OSXFreeBSDOpenBSDNetBSD-B也支持该选项,但是从技术上讲,它不是标准选项。grep


1
迈克尔·荷马-谢谢。我没有-B选项。还有其他想法吗?
戴尔

@Dale您可以安装GNU grep吗?这将为您提供选择。
2014年

9

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使用嵌入的\newline将附加的材料与原始材料分开,将 输入的下一行(减去其终止ewline)附加到图案空间。请注意,当前行号会更改。

[2addr]P 将模式空间(直到第一个\newline)写入标准输出。

[2addr]D 通过第一个\newline 删除模式空间的初始段,然后开始下一个循环。

因此,在第一行上,您需要在模式空间中添加一条额外的行,因此如下所示:

^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。

无论如何,你明白了。


感谢您的辛勤工作。抱歉,我要在其中搜索的文件名放在哪里?
Dale

@戴尔-我的坏。sed '...' $filename。顺便说一句-我从您自己的搜索字符串中删除了句点,但实际上不是模式中的句点-那些代表任何单个字符。您可能应该oct\.oct\.oct\.oct逃脱它们,以便它们匹配句点。
mikeserv

我尝试使用它和不同的<>符号,但未找到与其他解决方案有关的事件,因此我想知道我的操作系统是否与这些解决方案不兼容。
Dale 2014年

现在结果为-> N; /141.299.99.1/P; D':找不到事件。
Dale

@Dale-请查看更新。它应该可以帮助您。
mikeserv

4

由于您提到没有的-B选择grep,因此可以使用Perl(例如)来滑动4行的窗口:

perl -ne '
    push @window,$_;
    shift @window if @window > 4;
    print @window if /141\.299\.99\.1/
' your_file

Ramesh的答案与相似awk


我不确定我的Perl版本是否支持此功能,但可以尝试一下。非常感谢您抽出宝贵的时间回答我的问题-非常感谢!
Dale 2014年

@戴尔非常欢迎。我怀疑该代码是否利用了任何尖端的Perl功能。
Joseph R.

4

如果可用,您可以使用pcregrep

pcregrep -M '.*\n.*\n.*\n141.299.99.1' file

检查我是否有PCREGREP。我喜欢命令的紧凑性。非常感谢您的时间和精力。谢谢!!!
Dale

4

您可以在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 

我的外壳很旧-Steve Jobs Open Step。好主意,谢谢您的时间!!!戴尔
戴尔2014年

@Dale perl方法几乎可以在任何地方使用。请以这种方式告诉我们您的操作系统(将其添加到您的问题中),我们可以为您提供建议。
terdon

如果我复制您的Perl并将其放入NotePad并将其放在一行中,它将起作用!问题-如果我想在比赛模式前说10行,我该如何将3更改为10?谢谢!
Dale

我看到可以通过添加更多$ F [$ iX]语句来添加更多行。谢谢!
Dale

4

如果您的系统不支持grep上下文,则可以尝试使用ack-grep

ack -B 3 141.299.99.1 file

ack 是grep之类的工具,针对程序员进行了优化。


我喜欢命令的紧凑性,但是我的系统不支持在手册页中查找ack。好主意,非常感谢您的宝贵时间!!!戴尔
戴尔2014年

@戴尔:令人惊讶!您的操作系统是什么?如果有perl,可以使用ack
cuonglm 2014年

2
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

非常详细-非常感谢。我会尝试的。非常感谢您的宝贵时间!戴尔
戴尔2014年

我有一个测试文件,您的解决方案有效!但是问题是,当我在大型生产文件上运行该文件时,它的记录号太长,因此输出无法使用该命令。我在此页面顶部的原始命令有效,但仅找到一个实例。我感谢您的帮助。我可以用原始命令做些什么使它找到多个状态?
Dale

1

在大多数情况下,/141.299.99.1/还将匹配(例如)141a299q99+1141029969951因为.在正则表达式中可以表示任何字符。

使用/141[.]299[.]99[.]1/更安全,你可以在开始添加额外的背景下,整个正则表达式的结束,以确保它不匹配3141..12.104,等。


1
这是一个好点-我也考虑过这一点。尽管如此,我还是使用问询者提供的字符串作为已知的工作匹配项-并在有机会的时候亲自通知他。无论如何- 并非所有这些-Steeldriver的答案从一开始就引用了char匹配项。
mikeserv
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.