IT story

grep으로 한 줄에 두 줄을 일치시킵니다.

hot-time 2020. 5. 8. 08:23
반응형

grep으로 한 줄에 두 줄을 일치시킵니다.


grep두 개의 다른 문자열이 포함 된 줄을 일치시키는 데 사용하려고합니다 . 나는 다음을 시도했지만 이것은 내가 원하는 것이 아닌 string1 또는 string2포함하는 줄과 일치합니다 .

grep 'string1\|string2' filename

그렇다면 두 문자열을 모두grep 포함하는 줄만 어떻게 일치 합니까?


당신이 사용할 수있는 grep 'string1' filename | grep 'string2'

또는, grep 'string1.*string2\|string2.*string1' filename


나는 이것이 당신이 찾고있는 것이라고 생각합니다.

grep -E "string1|string2" filename

나는 다음과 같이 대답한다고 생각합니다.

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

둘 중 하나만 존재하거나 둘 다 존재하지 않는 경우에만 일치합니다.


모든 단어를 포함하는 파일을 임의의 순서로 검색하려면

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

첫 번째 grep은 재귀 적 검색 ( r)을 시작하고 대소 문자 ( i)를 무시 하고 파일의 어느 곳에서든 l한 용어 ( 'action'작은 따옴표)와 일치하는 ( ) 파일 이름을 나열 (인쇄) 합니다.

후속 greps는 다른 용어를 검색하여 대소 문자를 구분하지 않고 일치하는 파일을 나열합니다.

귀하가 받게 될 최종 파일 목록은 파일의 임의의 위치에 상관없이 이러한 용어를 포함하는 것들입니다.


당신이있는 경우 grep로모그래퍼 -P제한에 대한 옵션 perl정규식, 당신은 사용할 수 있습니다

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

겹치는 문자열로 작업 할 수 있다는 이점이 있습니다. and를 더 직접 지정할 수 있기 때문에 perlas를 사용 하는 것이 다소 간단 grep합니다.

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

귀하의 방법은 거의 좋았으며 -w 만 누락되었습니다.

grep -w 'string1\|string2' filename

|정규 표현식 연산자는 또는을 의미합니다. 즉, string1 또는 string2가 일치한다고합니다. 당신은 할 수 있습니다 :

grep 'string1' filename | grep 'string2'

첫 번째 명령의 결과를 두 번째 grep으로 파이프합니다. 그것은 둘 다 일치하는 줄만 줄 것입니다.


다음과 같이 시도해보십시오.

(pattern1.*pattern2|pattern2.*pattern1)

사람들이 펄과 파이썬, 복잡한 셸 스크립트를 제안한 것처럼 간단한 awk 접근법이 있습니다.

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

허용 된 답변에 대한 의견을 살펴본 결과 : 아니요, 여러 줄로 수행되지 않습니다. 그러나 그것은 또한 그 질문의 저자가 요구 한 것이 아닙니다.


6 개의 공백으로 시작하고 다음으로 끝나는 행을 찾았습니다.

 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 명령을 사용하십시오.

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

2) egrep 명령 사용

egrep -c 'DOG|CAT' testfile 

egrep을 사용하면 표현에 대해 걱정할 필요가 없으며 파이프 구분 기호로 단어를 구분하면됩니다.


grep을 사용하지 말고 대신 awk를 사용하십시오. grep에서 2 개의 정규 표현식 R1 및 R2를 일치 시키려면 다음과 같이 생각하십시오.

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

어색한 동안 :

awk '/R1/ && /R2/'

그러나 R2겹치거나 하위 집합 인 경우 어떻게해야 R1합니까? grep 명령은 awk 명령과 달리 작동하지 않습니다. 당신이 포함하는 행을 찾으려면 말할 수 있습니다 theheat:

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

이를 위해 2 개의 그렙과 파이프를 사용해야합니다.

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

물론 실제로 분리되도록 요구 한 경우 grep에서 사용한 것과 동일한 정규 표현식을 항상 awk로 작성할 수 있으며 가능한 모든 시퀀스에서 정규 표현식을 반복하지 않는 대체 awk 솔루션이 있습니다.

이를 제쳐두고, 3 개의 정규 표현식 R1, R2 및 R3에 맞게 솔루션을 확장하려면 어떻게해야합니까? grep에서는 다음과 같은 잘못된 선택 중 하나입니다.

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 '/R1/ && /R2/ && /R3/'

이제 정규 표현식 R1 및 R2 대신 리터럴 문자열 S1 및 S2를 실제로 일치 시키려면 어떻게해야합니까? grep을 한 번만 호출하면 단순히 grep을 호출 할 수 없습니다. grep을 호출하기 전에 모든 RE 메타 문자를 이스케이프하는 코드를 작성해야합니다.

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

또는 다시 2 그렙과 파이프를 사용하십시오.

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

awk에서는 regexp 연산자 대신 문자열 연산자를 사용하면됩니다.

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

이제 한 줄이 아닌 단락에서 2 개의 정규 표현식을 일치 시키려면 어떻게해야합니까? grep, awk에서 사소한 일을 할 수 없습니다 :

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

How about across a whole file? Again can't be done in grep and trivial in awk (this time I'm using GNU awk for multi-char RS for conciseness but it's not much more code in any awk or you can pick a control-char you know won't be in the input for the RS to do the same):

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

So - if you want to find multiple regexps or strings in a line or paragraph or file then don't use grep, use awk.


grep ‘string1\|string2’ FILENAME 

GNU grep version 3.1


Place the strings you want to grep for into a file

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

Then search using -f

grep -f find.txt BIG_FILE_TO_SEARCH.txt 

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

will get line with string1 and string2 in any order


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

This works for exact word match and matching case insensitive words ,for that -i is used


git grep

Here is the syntax using git grep with multiple patterns:

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

You may also combine patterns with Boolean expressions such as --and, --or and --not.

Check man git-grep for help.


--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.

Other params to consider:

--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.

To change the pattern type, you may also use -G/--basic-regexp (default), -F/--fixed-strings, -E/--extended-regexp, -P/--perl-regexp, -f file, and other.

Related:

For OR operation, see:


for multiline match:

echo -e "test1\ntest2\ntest3" |tr -d '\n' |grep "test1.*test3"

or

echo -e "test1\ntest5\ntest3" >tst.txt
cat tst.txt |tr -d '\n' |grep "test1.*test3\|test3.*test1"

we just need to remove the newline character and it works!


You should have grep like this:

$ grep 'string1' file | grep 'string2'

I often run into the same problem as yours, and I just wrote a piece of script:

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
}

Usage:

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

You can put it in .bashrc if you like.


ripgrep

Here is the example using rg:

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

It's one of the quickest grepping tools, since it's built on top of Rust's regex engine which uses finite automata, SIMD and aggressive literal optimizations to make searching very fast.

Use it, especially when you're working with a large data.

See also related feature request at GH-875.

참고URL : https://stackoverflow.com/questions/4487328/match-two-strings-in-one-line-with-grep

반응형