关于正则表达式:使用grep匹配一行中的两个字符串

Match two strings in one line with grep

我试图使用grep来匹配包含两个不同字符串的行。 我尝试了以下但是这匹配包含string1或string2的行,这不是我想要的。

1
grep 'string1\|string2' filename

那么我如何只与包含两个字符串的行匹配grep


我认为这就是你要找的东西:

1
grep -E"string1|string2" filename

我认为答案是这样的:

1
grep 'string1.*string2\|string2.*string1' filename

只匹配两者都存在的情况,而不是一个或另一个或两者。


您可以使用grep 'string1' filename | grep 'string2'

或者,grep 'string1.*string2\|string2.*string1' filename


要在任何地方搜索包含所有单词的文件:

1
grep -ril \'action\' | xargs grep -il \'model\' | xargs grep -il \'view_type\'

第一个grep启动递归搜索(r),忽略大小写(i)并列出(打印出)一个术语匹配(l)的文件的名称('action'与单个引号)发生在文件中的任何位置。

随后的greps搜索其他术语,保留不区分大小写并列出匹配的文件。

您将获得的最终文件列表将是包含这些术语的文件,可以按文件中的任何顺序排列。


如果你有一个grep-P选项用于限制perl正则表达式,你可以使用

1
grep -P '(?=.*string1)(?=.*string2)'

它具有处理重叠字符串的优点。使用perl作为grep更简单一些,因为您可以更直接地指定和逻辑:

1
perl -ne 'print if /string1/ && /string2/'


你的方法几乎是好的,只缺少-w

1
grep -w 'string1\|string2' filename


你可以尝试这样的事情:

1
(pattern1.*pattern2|pattern2.*pattern1)

正则表达式中的|运算符表示或。也就是说string1或string2匹配。你可以这样做:

1
grep 'string1' filename | grep 'string2'

这会将第一个命令的结果传递给第二个grep。这应该只给你两条线匹配。


正如人们建议使用perl和python以及复杂的shell脚本,这里有一个简单的awk方法:

1
awk '/string1/ && /string2/' filename

看了接受答案的评论:不,这不做多行;但那也不是问题的作者所要求的。


找到仅以6个空格开头的行并完成:

1
2
3
4
 cat my_file.txt | grep
 -e '^      .*(\.c$|\.cpp$|\.h$|\.log$|\.out$)' # .c or .cpp or .h or .log or .out
 -e '^      .*[0-9]\{5,9\}$' # numers between 5 and 9 digist
 > nolog.txt


1
grep ‘string1\|string2’ FILENAME

GNU grep版本3.1


假设我们需要在文件测试文件中找到多个单词的计数。
有两种方法可以解决它

1)使用带有正则表达式匹配模式的grep命令

1
grep -c '\<\(DOG\|CAT\)\>' testfile

2)使用egrep命令

1
egrep -c 'DOG|CAT' testfile

使用egrep,您不必担心表达式,只需通过管道分隔符分隔单词。


不要尝试使用grep,而是使用awk。要在grep中匹配2个正则表达式R1和R2,您认为它将是:

1
grep 'R1.*R2|R2.*R1'

而在awk它是:

1
awk '/R1/ && /R2/'

但是如果R2R1重叠或是R1的子集呢?当awk命令执行时,那个grep命令根本不起作用。假设您要查找包含theheat的行:

1
2
3
$ echo 'theatre' | grep 'the.*heat|heat.*the'
$ echo 'theatre' | awk '/the/ && /heat/'
theatre

你必须使用2个greps和一个管道:

1
2
$ echo 'theatre' | grep 'the' | grep 'heat'
theatre

当然,如果你实际上要求它们是分开的,你总是可以在awk中写入与grep中使用的相同的正则表达式,并且有其他awk解决方案不涉及在每个可能的序列中重复正则表达式。

抛开这一点,如果您想扩展您的解决方案以匹配3个正则表达式R1,R2和R3,该怎么办?在grep中,这将是这些糟糕的选择之一:

1
2
grep 'R1.*R2.*R3|R1.*R3.*R2|R2.*R1.*R3|R2.*R3.*R1|R3.*R1.*R2|R3.*R2.*R1' file
grep R1 file | grep R2 | grep R3

而在awk中,它是简洁,明显,简单,高效的:

1
awk '/R1/ && /R2/ && /R3/'

现在,如果您真的想要匹配文字字符串S1和S2而不是正则表达式R1和R2,该怎么办?你只是不能在一次grep调用中做到这一点,你必须在调用grep之前编写代码以逃避所有RE metachars:

1
2
3
S1=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R1')
S2=$(sed 's/[^^]/[&]/g; s/\^/\\^/g' <<< 'R2')
grep 'S1.*S2|S2.*S1'

或者再次使用2个greps和一个管道:

1
grep -F 'S1' file | grep -F 'S2'

这又是糟糕的选择,而使用awk你只需使用字符串运算符而不是regexp运算符:

1
awk 'index($0,S1) && index($0.S2)'

现在,如果你想在一个段落而不是一行中匹配2个正则表达式怎么办?在awk中无法完成,在awk中无关紧要:

1
awk -v RS='' '/R1/ && /R2/'

整个文件怎么样?再次无法用awk中的grep和trivial完成(这次我使用GNU awk来实现多字符RS的简洁性,但是在任何awk中代码都不多,或者你可以选择一个你不知道的控件字符在RS的输入中做同样的事情):

1
awk -v RS='^$' '/R1/ && /R2/'

所以 - 如果你想在一行或一个段落或文件中找到多个正则表达式或字符串,那么不要使用grep,请使用awk。


1
grep '(string1.*string2 | string2.*string1)' filename

将以任何顺序获得string1和string2的行


1
grep -i -w 'string1\|string2' filename

这适用于精确的单词匹配和匹配不区分大小写的单词,因为使用了-i


git grep

以下是使用带有多个模式的git grep的语法:

1
git grep --all-match --no-index -l -e string1 -e string2 -e string3 file

您还可以将模式与布尔表达式(如--and--or--not)组合在一起。

检查man git-grep以获取帮助。

--all-match When giving multiple pattern expressions, this flag is specified to limit the match to files that have lines to match all of them.

--no-index Search files in the current directory that is not managed by Git.

-l/--files-with-matches/--name-only Show only the names of files.

-e The next parameter is the pattern. Default is to use basic regexp.

其他参考考虑:

--threads Number of grep worker threads to use.

-q/--quiet/--silent Do not output matched lines; exit with status 0 when there is a match.

要更改图案类型,您还可以使用-G / --basic-regexp(默认),-F / --fixed-strings-E / --extended-regexp-P / --perl-regexp-f file和其他。

有关:

  • 如何grep同一行上存在的两个单词?
  • 检查文件中是否存在多个字符串或正则表达式
  • 如何使用多个AND模式运行grep?并立即匹配文件中的所有模式

对于OR操作,请参阅:

  • 如何使用具有管道字符的图案grep多个图案?
  • Grep:如何添加"OR"条件?


将要grep的字符串放入文件中

1
2
3
echo who    > find.txt
echo Roger >> find.txt
echo [44][0-9]{9,} >> find.txt

然后使用-f搜索

1
grep -f find.txt BIG_FILE_TO_SEARCH.txt

我经常遇到和你一样的问题,我刚写了一段脚本:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
function m() { # m means 'multi pattern grep'

    function _usage() {
    echo"usage: COMMAND [-inH] -p<pattern1> -p<pattern2> <filename>"
    echo"-i : ignore case"
    echo"-n : show line number"
    echo"-H : show filename"
    echo"-h : show header"
    echo"-p : specify pattern"
    }

    declare -a patterns
    # it is important to declare OPTIND as local
    local ignorecase_flag  filename linum header_flag colon result OPTIND

    while getopts"iHhnp:" opt; do
    case $opt in
        i)
        ignorecase_flag=true ;;
        H)
        filename="FILENAME," ;;
        n)
        linum="NR," ;;
        p)
        patterns+=("$OPTARG" ) ;;
        h)
        header_flag=true ;;
        \?)
        _usage
        return ;;
    esac
    done

    if [[ -n $filename || -n $linum ]]; then
    colon="":","
    fi

    shift $(( $OPTIND - 1 ))

    if [[ $ignorecase_flag == true ]]; then
    for s in"${patterns[@]}"; do
            result+=" && s~/${s,,}/"
    done
    result=${result# && }
    result="{s=tolower(\$0)} $result"
    else
    for s in"${patterns[@]}"; do
            result="$result && /$s/"
    done
    result=${result# && }
    fi

    result+=" { print"$filename$linum$colon"\$0 }"

    if [[ ! -t 0 ]]; then       # pipe case
    cat - | awk"${result}"
    else
    for f in"$@"; do
        [[ $header_flag == true ]] && echo"########## $f ##########"
        awk"${result}" $f
    done
    fi
}

用法:

1
2
echo"a b c" | m -p A
echo"a b c" | m -i -p A # a b c

如果你愿意,可以把它放在.bashrc中。


用于多行匹配:

1
2
3
4
echo -e"test1
test2
test3" |tr -d '
' |grep"test1.*test3"

要么

1
2
3
4
5
echo -e"test1
test5
test3">tst.txt
cat tst.txt |tr -d '
' |grep"test1.*test3\|test3.*test1"

我们只需要删除换行符就行了!


你应该像这样grep

1
$ grep 'string1' file | grep 'string2'


ripgrep

以下是使用rg的示例:

1
rg -N '(?P<p1>.*string1.*)(?P<p2>.*string2.*)' file.txt

它是最快的grepping工具之一,因为它建立在Rust的正则表达式引擎之上,它使用有限自动机,SIMD和积极的文字优化来快速搜索。

使用它,尤其是当您处理大数据时。

另请参阅GH-875的相关功能请求。