我想找到有“abc”和“efg”的文件,这两个字符串在该文件中的不同行。一个包含以下内容的文件:
blah blah.. blah blah.. blah abc blah blah blah.. blah blah.. blah blah.. blah efg blah blah blah blah.. blah blah..
应该匹配。
我不知道如何用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文档:
grep
grep搜索已命名的输入FILEs(或标准输入,如果没有文件被命名,或如果一个连字符减号(-)被作为文件名),以查找包含与给定PATTERN匹配的行。
如果可以使用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
我不确定是否可以使用grep,但sed使它非常简单:
sed -e '/abc/,/efg/!d' [file-with-content]
Grep是这种操作的笨拙工具。
pcregrep在大多数现代Linux系统中都可以被用作
pcregrep -M 'abc.*(\n|.)*efg' test.txt
哪里-M, --multiline允许模式匹配多行
-M
--multiline
还有一个更新的pcre2grep。两者都由PCRE项目. xml提供。
pcre2grep可用于Mac OS X,通过Mac港口作为端口pcre2的一部分:
pcre2
% sudo port install pcre2
和通过家酿作为:
% brew install pcre
或者pcre2
% brew install pcre2
pcre2grep是Linux上的也可用 (Ubuntu 18.04+)
$ sudo apt install pcre2-utils # PCRE2 $ sudo apt install pcregrep # Older PCRE
#!/bin/bash shopt -s nullglob for file in * do r=$(awk '/abc/{f=1}/efg/{g=1;exit}END{print g&&f ?1:0}' file) if [ "$r" -eq 1 ];then echo "Found pattern in $file" else echo "not found" fi done
虽然sed选项是最简单、最简单的,但遗憾的是,LJ的一行程序并不是最可移植的。那些受困于C Shell(而不是bash)版本的人将需要摆脱他们的刘海:
sed -e '/abc/,/efg/\!d' [file]
不幸的是,这一行在bash等中不起作用。
如果您对模式序列不感兴趣,可以使用grep。
grep -l "pattern1" filepattern*.* | xargs grep "pattern2"
例子
grep -l "vector" *.cpp | xargs grep "map"
grep -l将找到所有与第一个模式匹配的文件,xargs将为第二个模式grep。希望这能有所帮助。
grep -l
awk一行程序:
awk '/abc/,/efg/' [file-with-content]
作为Balu Mohan的答案的替代方案,可以只使用grep, head和tail来强制模式的顺序:
head
tail
for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep "pattern2" &>/dev/null && echo $f; done
不过,这个不太漂亮。格式化得更容易读:
for f in FILEGLOB; do tail $f -n +$(grep -n "pattern1" $f | head -n1 | cut -d : -f 1) 2>/dev/null \ | grep -q "pattern2" \ && echo $f done
这将打印所有"pattern2"出现在"pattern1", 或者两者出现在同一行之后的文件名称:
"pattern2"
"pattern1"
$ echo "abc def" > a.txt $ echo "def abc" > b.txt $ echo "abcdef" > c.txt; echo "defabc" > d.txt $ for f in *.txt; do tail $f -n +$(grep -n "abc" $f | head -n1 | cut -d : -f 1) 2>/dev/null | grep -q "def" && echo $f; done a.txt c.txt d.txt
tail -n +i
grep -n
head -n1
cut -d : -f 1
:
2>/dev/null
$()
grep -q
如果您愿意使用上下文,这可以通过输入来实现
grep -A 500 abc test.txt | grep -B 500 efg
这将显示之间的 "abc"和"efg"的所有内容,只要它们之间的距离在500行以内。
下面是一个受这个答案启发的解决方案:
如果'abc'和'efg'可以在同一行:
grep -zl 'abc.*efg' <your list of files>
如果'abc'和'efg'必须在不同的行:
grep -Pzl '(?s)abc.*\n.*efg' <your list of files>
参数:
-z将输入视为一组行,每一行以0字节结束,而不是换行符。例如,grep将输入视为一行。注意,如果你不使用-l,它将显示匹配后的NUL字符,见注释。
-z
-l
-l只列出匹配的文件名。
(?s)激活PCRE_DOTALL,这意味着'。'查找任何字符或换行符。
(?s)
sed应该足够了,就像海报LJ上面说的,
而不是!d,你可以简单地使用p打印:
sed -n '/abc/,/efg/p' file
银色的搜索器:
ag 'abc.*(\n|.)*efg' your_filename
与戒指持有者的答案相似,但用ag代替。银色搜索者的速度优势可能在这里大放异彩。
我在几天前发布了一个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上找到更多信息。
如果你需要两个单词彼此接近,例如不超过3行,你可以这样做:
find . -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
同样的例子,但是只过滤*.txt文件:
find . -name *.txt -exec grep -Hn -C 3 "abc" {} \; | grep -C 3 "efg"
你也可以用egrep命令替换grep命令,如果你想找到正则表达式。
egrep
我非常依赖于pcregrep,但是对于更新的grep,您不需要安装它的许多特性。只需使用grep -P即可。
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,看看顶部是否出现类似的内容:
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 -lpne 'print $ARGV if /abc.*?efg/s' file_list
$ARGV包含从file_list读取当前文件时的文件名 /s修饰符跨换行搜索。< / p >
$ARGV
file_list读取当前文件时的文件名 /s
这很容易做到,首先使用tr将换行符替换为其他字符:
tr
tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'
\a
.
这应该可以工作:
cat FILE | egrep 'abc|efg'
如果有多个匹配项,可以使用grep -v过滤掉
文件模式*.sh对于防止目录被检查很重要。当然,一些测试也可以防止这种情况发生。
*.sh
for f in *.sh do a=$( grep -n -m1 abc $f ) test -n "${a}" && z=$( grep -n efg $f | tail -n 1) || continue (( ((${z/:*/}-${a/:*/})) > 0 )) && echo $f done
的
grep -n -m1 abc $f
搜索最大匹配1并返回(-n)行数。 如果找到一个匹配(test -n…),找到efg的最后一个匹配(找到所有,最后一个尾部为-n 1).
z=$( grep -n efg $f | tail -n 1)
其他的继续。
由于结果是类似18:foofile.sh String alf="abc";的东西,我们需要从“:”截断到行尾。
18:foofile.sh String alf="abc";
((${z/:*/}-${a/:*/}))
如果第二个表达式的最后一个匹配超过了第一个表达式的第一个匹配,则应返回正结果。
然后我们报告文件名echo $f。
echo $f
我用它从一个multi fasta文件中提取一个fasta序列,使用grep的-P选项:
grep -Pzo ">tig00000034[^>]+" file.fasta > desired_sequence.fasta
regexp的核心是[^>],它翻译为“not The greater than symbol”。
[^>]
如果你对你要找的两个字符串'abc'和'efg'之间的距离有一些估计,你可以使用:
grep -r . -e 'abc' -A num1 -B num2 | grep 'efg'
下面是一种连续使用两个grep的方法:
egrep -o 'abc|efg' $file | grep -A1 abc | grep efg | wc -l
返回0或正整数。
egrep -o(只显示匹配,技巧:同一行上的多个匹配会产生多行输出,就好像它们在不同的行上一样)
grep -A1 abc(打印abc及其后面的行)
grep -A1 abc
grep efg | wc -l(在abc之后找到的相同或后面的efg行数为0-n,结果可用于'if")
grep efg | wc -l
如果需要模式匹配,grep可以更改为egrep等
随着几个月前发布的ugrep:
ugrep 'abc(\n|.)+?efg'
这个工具是高度优化的速度。它也是GNU/BSD/PCRE-grep兼容的。
注意,我们应该使用惰性重复+?,除非你想将所有带有efg的行匹配在一起,直到文件中的最后一个efg。
+?
efg
在所有文件中递归搜索(在每个文件中的多行中),同时存在两个字符串(即string1和string2在不同的行中,并且都存在于同一个文件中):
grep -r -l 'string1' * > tmp; while read p; do grep -l 'string2' $p; done < tmp; rm tmp
在所有文件中递归搜索(在每个文件中的多行中),使用EITHER字符串存在(即string1和string2在不同的行中,并且在同一个文件中存在):
grep -r -l 'string1\|string2' *
你至少有几个选择
example-text:
true match me false match me one false match me two true match me three third line!! {BLANK_LINE}
命令:
grep -Pozi '(?s)true.+?\n(?=\n)' example-text
-p用于perl正则表达式
-o只匹配模式,而不是整行
-z允许换行
-i不区分大小写
输出:
true match me true match me three third line!!
注:
- +? makes modifier non-greedy so matches shortest string instead of largest (prevents from returning one match containing entire text)
grep -Pozi 'true(.|\n)+?\n(?=\n)'
使用ripgrep可以:
$ rg --multiline 'abc(\n|.)+?efg' test.txt 3:blah abc blah 4:blah abc blah 5:blah blah.. 6:blah blah.. 7:blah blah.. 8:blah efg blah blah
或者其他咒语。
如果你想让.作为换行符:
$ rg --multiline '(?s)abc.+?efg' test.txt 3:blah abc blah 4:blah abc blah 5:blah blah.. 6:blah blah.. 7:blah blah.. 8:blah efg blah blah
或者与(?s)等价的是rg --multiline --multiline-dotall
rg --multiline --multiline-dotall
为了回答最初的问题,它们必须在不同的行上:
$ rg --multiline 'abc.*[\n](\n|.)*efg' test.txt
如果你想要“不贪婪”;所以你不只是得到第一个ABC和最后一个efg(把它们分成一对):
$ rg --multiline 'abc.*[\n](\n|.)*?efg' test.txt
https://til.hashrocket.com/posts/9zneks2cbv-multiline-matches-with-ripgrep-rg