我想找到有“abc”和“efg”的文件,这两个字符串在该文件中的不同行。一个包含以下内容的文件:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
应该匹配。
我想找到有“abc”和“efg”的文件,这两个字符串在该文件中的不同行。一个包含以下内容的文件:
blah blah..
blah blah..
blah abc blah
blah blah..
blah blah..
blah blah..
blah efg blah blah
blah blah..
blah blah..
应该匹配。
当前回答
虽然sed选项是最简单、最简单的,但遗憾的是,LJ的一行程序并不是最可移植的。那些受困于C Shell(而不是bash)版本的人将需要摆脱他们的刘海:
sed -e '/abc/,/efg/\!d' [file]
不幸的是,这一行在bash等中不起作用。
其他回答
我不知道如何用grep做到这一点,但我会用awk做这样的事情:
awk '/abc/{ln1=NR} /efg/{ln2=NR} END{if(ln1 && ln2 && ln1 < ln2){print "found"}else{print "not found"}}' foo
不过,你需要注意如何做到这一点。您希望正则表达式匹配子字符串还是整个单词?适当添加\w标记。此外,虽然这严格符合您陈述的示例,但当abc在efg之后第二次出现时,它并不完全有效。如果你想处理这个问题,在/abc/ case等中添加一个If。
我在几天前发布了一个grep替代方案,它直接支持这一点,通过多行匹配或使用条件——希望它对搜索这里的人有用。下面是示例命令的样子:
多行:
sift -lm 'abc.*efg' testfile
条件:
sift -l 'abc' testfile --followed-by 'efg'
你也可以指定'efg'必须在一定的行数内跟在'abc'后面:
sift -l 'abc' testfile --followed-within 5:'efg'
你可以在sift-tool.org上找到更多信息。
遗憾的是,你不能。来自grep文档:
grep搜索指定的输入FILEs(或标准输入,如果没有指定文件,或如果给出了一个连字符减号(-)作为文件名),以查找包含与给定PATTERN匹配的行。
我非常依赖于pcregrep,但是对于更新的grep,您不需要安装它的许多特性。只需使用grep -P。
在OP的问题的例子中,我认为以下选项很好地发挥了作用,第二好的选项符合我对问题的理解:
grep -Pzo "abc(.|\n)*efg" /tmp/tes*
grep -Pzl "abc(.|\n)*efg" /tmp/tes*
我将文本复制为/tmp/test1,删除'g'并保存为/tmp/test2。下面的输出显示,第一个显示匹配的字符串,第二个只显示文件名(典型的-o显示匹配,典型的-l只显示文件名)。请注意,'z'对于多行是必要的,'(.|\n)'意味着匹配'换行符以外的任何内容'或'换行符' -即任何内容:
user@host:~$ grep -Pzo "abc(.|\n)*efg" /tmp/tes*
/tmp/test1:abc blah
blah blah..
blah blah..
blah blah..
blah efg
user@host:~$ grep -Pzl "abc(.|\n)*efg" /tmp/tes*
/tmp/test1
要确定你的版本是否足够新,运行man grep,看看顶部是否出现类似的内容:
-P, --perl-regexp
Interpret PATTERN as a Perl regular expression (PCRE, see
below). This is highly experimental and grep -P may warn of
unimplemented features.
它来自GNU grep 2.10。
如果可以使用Perl,就可以很容易地做到这一点。
perl -ne 'if (/abc/) { $abc = 1; next }; print "Found in $ARGV\n" if ($abc && /efg/); }' yourfilename.txt
您也可以使用单个正则表达式来实现这一点,但这涉及到将文件的整个内容放入单个字符串中,对于大型文件,这可能会占用太多内存。 为了完整起见,下面是该方法:
perl -e '@lines = <>; $content = join("", @lines); print "Found in $ARGV\n" if ($content =~ /abc.*efg/s);' yourfilename.txt