복붙노트

[PYTHON] 모든 문자열이나 정규 표현식이 파일에 존재하는지 확인

PYTHON

모든 문자열이나 정규 표현식이 파일에 존재하는지 확인

모든 문자열이 텍스트 파일에 있는지 확인하고 싶습니다. 같은 줄에 있거나 다른 줄에 존재할 수 있습니다. 부분 일치는 괜찮을 것입니다. 이렇게 :

...
string1
...
string2
...
string3
...
string1 string2
...
string1 string2 string3
...
string3 string1 string2
...
string2 string3
... and so on

위의 예에서 문자열 대신 정규식을 사용할 수 있습니다.

예를 들어, 다음 코드는 파일에 내 문자열이 있는지 확인합니다.

if grep -EFq "string1|string2|string3" file; then
  # there is at least one match
fi

그들 모두가 존재하는지 확인하는 방법? 우리는 모든 성냥의 존재에 관심이 있기 때문에, 모든 문자열이 일치하자마자 파일 읽기를 중단해야합니다.

grep을 여러 번 호출하지 않고도 (입력 파일이 크거나 일치 할 문자열이 많거나 awk 또는 python과 같은 도구를 사용하지 않아도됩니다)?

또한 regexes 용으로 쉽게 확장 할 수있는 문자열을위한 솔루션이 있습니까?

해결법

  1. ==============================

    1.Awk는 grep, shell 등을 발명 한 사람들이 이와 같은 일반 텍스트 조작 작업을하기 위해 발명 한 도구이므로 왜 이것을 피하려고 노력해야하는지 확신 할 수 없습니다.

    Awk는 grep, shell 등을 발명 한 사람들이 이와 같은 일반 텍스트 조작 작업을하기 위해 발명 한 도구이므로 왜 이것을 피하려고 노력해야하는지 확신 할 수 없습니다.

    간결함을 찾으려는 분이라면, GNU awk one-liner가 여러분이 원하는 것을 할 수 있습니다 :

    awk 'NR==FNR{a[$0];next} {for(s in a) if(!index($0,s)) exit 1}' strings RS='^$' file
    

    여기에 다른 정보와 옵션이 있습니다.

    당신이 정말로 문자열을 찾고 있다고 가정하면, 그것은 다음과 같을 것입니다 :

    awk -v strings='string1 string2 string3' '
    BEGIN {
        numStrings = split(strings,tmp)
        for (i in tmp) strs[tmp[i]]
    }
    numStrings == 0 { exit }
    {
        for (str in strs) {
            if ( index($0,str) ) {
                delete strs[str]
                numStrings--
            }
        }
    }
    END { exit (numStrings ? 1 : 0) }
    ' file
    

    위의 코드는 모든 문자열이 일치하는 즉시 파일 읽기를 중지합니다.

    문자열 대신 regexps를 찾고 나서 다중 문자 RS에 GNU awk를 사용하고 END 섹션에 $ 0을 보유하면 다음을 할 수 있습니다.

    awk -v RS='^$' 'END{exit !(/regexp1/ && /regexp2/ && /regexp3/)}' file
    

    사실, 문자열 일지라도 다음과 같이 할 수 있습니다.

    awk -v RS='^$' 'END{exit !(index($0,"string1") && index($0,"string2") && index($0,"string3"))}' file
    

    위의 두 가지 GNU awk 솔루션의 가장 큰 문제점은 @ anubhava의 GNU grep -P 솔루션과 같이 전체 파일을 한 번에 메모리에 읽어야하는 반면 위의 첫 번째 awk 스크립트에서는 모든 awk에서 작동한다는 것입니다 모든 유닉스 셸의 셸은 한 번에 한 줄의 입력 만 저장합니다.

    나는 당신이 당신의 질문에 당신이 수천 개의 "패턴"을 가질 수 있다고 말하기 위해 코멘트를 추가 한 것을 본다. "문자열"을 의미한다고 가정하면 스크립트에 인수로 전달하는 대신 파일 (예 : 파일)에서 읽을 수 있습니다. 다중 문자 RS를위한 GNU awk와 한 줄에 하나의 검색 문자열을 가진 파일 :

    awk '
    NR==FNR { strings[$0]; next }
    {
        for (string in strings)
            if ( !index($0,string) )
                exit 1
    }
    ' file_of_strings RS='^$' file_to_be_searched
    

    regexps의 경우 :

    awk '
    NR==FNR { regexps[$0]; next }
    {
        for (regexp in regexps)
            if ( $0 !~ regexp )
                exit 1
    }
    ' file_of_regexps RS='^$' file_to_be_searched
    

    GNU awk가없고 입력 파일에 NUL 문자가 포함되어 있지 않으면 RS = '^ $'대신 RS = '\ 0'을 사용하거나 위의 변수를 한 줄에 추가하여 같은 효과를 얻을 수 있습니다 읽은 시간과 END 섹션에서 해당 변수를 처리하는 시간.

    file_to_be_searched가 너무 커서 메모리에 들어 가지 않으면 문자열에 대해 다음과 같이됩니다.

    awk '
    NR==FNR { strings[$0]; numStrings=NR; next }
    numStrings == 0 { exit }
    {
        for (string in strings) {
            if ( index($0,string) ) {
                delete strings[string]
                numStrings--
            }
        }
    }
    END { exit (numStrings ? 1 : 0) }
    ' file_of_strings file_to_be_searched
    

    regexps에 해당하는 :

    awk '
    NR==FNR { regexps[$0]; numRegexps=NR; next }
    numRegexps == 0 { exit }
    {
        for (regexp in regexps) {
            if ( $0 ~ regexp ) {
                delete regexps[regexp]
                numRegexps--
            }
        }
    }
    END { exit (numRegexps ? 1 : 0) }
    ' file_of_regexps file_to_be_searched
    
  2. ==============================

    2.다음은 여러 패턴과 함께 git grep을 사용하는 구문입니다.

    다음은 여러 패턴과 함께 git grep을 사용하는 구문입니다.

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

    패턴을 - and, --or 및 --not과 같은 부울 표현식과 결합 할 수도 있습니다.

    man git-grep에서 도움을 받으십시오.

    고려해야 할 다른 매개 변수 :

    패턴 유형을 변경하려면 -G / - 기본 -regexp (기본값), -F / - 고정 문자열, -E / - extended-regexp, -P / - perl-regexp, f 파일 및 기타.

  3. ==============================

    3.이 gnu-awk 스크립트가 작동 할 수 있습니다 :

    이 gnu-awk 스크립트가 작동 할 수 있습니다 :

    cat fileSearch.awk
    re == "" {
       exit
    }
    {
       split($0, null, "\\<(" re "\\>)", b)
       for (i=1; i<=length(b); i++)
          gsub("\\<" b[i] "([|]|$)", "", re)
    }
    END {
       exit (re != "")
    }
    

    그런 다음 다음과 같이 사용하십시오.

    if awk -v re='string1|string2|string3' -f fileSearch.awk file; then
       echo "all strings were found"
    else
       echo "all strings were not found"
    fi
    

    또는 PCRE 옵션과 함께이 gnu grep 솔루션을 사용할 수 있습니다.

    grep -qzP '(?s)(?=.*\bstring1\b)(?=.*\bstring2\b)(?=.*\bstring3\b)' file
    

    남자 1 인당 잡아 :

    -z, --null-data
       Treat  input  and  output  data as sequences of lines, each terminated by a 
       zero byte (the ASCII NUL character) instead of a newline.
    
  4. ==============================

    4.먼저 awk을 사용하고 싶을 것이다. 질문 문에서 해당 옵션을 제거 했으므로 가능하며이를 수행 할 수있는 방법이 제공됩니다. 그것은 awk를 사용하는 것보다 훨씬 느릴 수 있지만 어쨌든 그것을하고 싶다면 ...

    먼저 awk을 사용하고 싶을 것이다. 질문 문에서 해당 옵션을 제거 했으므로 가능하며이를 수행 할 수있는 방법이 제공됩니다. 그것은 awk를 사용하는 것보다 훨씬 느릴 수 있지만 어쨌든 그것을하고 싶다면 ...

    이는 다음 가정에 기초합니다. G

    이것은 모든 요구 사항을 충족시킬 수 있습니다 : (정규 표현식 버전은 약간의 코멘트를 놓치지 만 대신 문자열 버전을보십시오)

    #!/bin/bash
    
    multimatch() {
        filename="$1" # Filename is first parameter
        shift # move it out of the way that "$@" is useful
        strings=( "$@" ) # search strings into an array
    
        declare -a matches # Array to keep track which strings already match
    
        # Initiate array tracking what we have matches for
        for ((i=0;i<${#strings[@]};i++)); do
            matches[$i]=0
        done
    
        while IFS= read -r line; do # Read file linewise
            foundmatch=0 # Flag to indicate whether this line matched anything
            for ((i=0;i<${#strings[@]};i++)); do # Loop through strings indexes
                if [ "${matches[$i]}" -eq 0 ]; then # If no previous line matched this string yet
                    string="${strings[$i]}" # fetch the string
                    if [[ $line = *$string* ]]; then # check if it matches
                        matches[$i]=1   # mark that we have found this
                        foundmatch=1    # set the flag, we need to check whether we have something left
                    fi
                fi
            done
            # If we found something, we need to check whether we
            # can stop looking
            if [ "$foundmatch" -eq 1 ]; then
                somethingleft=0 # Flag to see if we still have unmatched strings
                for ((i=0;i<${#matches[@]};i++)); do
                    if [ "${matches[$i]}" -eq 0 ]; then
                        somethingleft=1 # Something is still outstanding
                        break # no need check whether more strings are outstanding
                    fi
                done
                # If we didn't find anything unmatched, we have everything
                if [ "$somethingleft" -eq 0 ]; then return 0; fi
            fi
        done < "$filename"
    
        # If we get here, we didn't have everything in the file
        return 1
    }
    
    multimatch_regex() {
        filename="$1" # Filename is first parameter
        shift # move it out of the way that "$@" is useful
        regexes=( "$@" ) # Regexes into an array
    
        declare -a matches # Array to keep track which regexes already match
    
        # Initiate array tracking what we have matches for
        for ((i=0;i<${#regexes[@]};i++)); do
            matches[$i]=0
        done
    
        while IFS= read -r line; do # Read file linewise
            foundmatch=0 # Flag to indicate whether this line matched anything
            for ((i=0;i<${#strings[@]};i++)); do # Loop through strings indexes
                if [ "${matches[$i]}" -eq 0 ]; then # If no previous line matched this string yet
                    regex="${regexes[$i]}" # Get regex from array
                    if [[ $line =~ $regex ]]; then # We use the bash regex operator here
                        matches[$i]=1   # mark that we have found this
                        foundmatch=1    # set the flag, we need to check whether we have something left
                    fi
                fi
            done
            # If we found something, we need to check whether we
            # can stop looking
            if [ "$foundmatch" -eq 1 ]; then
                somethingleft=0 # Flag to see if we still have unmatched strings
                for ((i=0;i<${#matches[@]};i++)); do
                    if [ "${matches[$i]}" -eq 0 ]; then
                        somethingleft=1 # Something is still outstanding
                        break # no need check whether more strings are outstanding
                    fi
                done
                # If we didn't find anything unmatched, we have everything
                if [ "$somethingleft" -eq 0 ]; then return 0; fi
            fi
        done < "$filename"
    
        # If we get here, we didn't have everything in the file
        return 1
    }
    
    if multimatch "filename" string1 string2 string3; then
        echo "file has all strings"
    else
        echo "file miss one or more strings"
    fi
    
    if multimatch_regex "filename" "regex1" "regex2" "regex3"; then
        echo "file match all regular expressions"
    else
        echo "file does not match all regular expressions"
    fi
    

    나는 "void", "function"및 "#define"문자열에 대해 Linux 4.16.2에서 arch / arm / .c, .h 및 .sh를 검색하는 벤치마킹을 수행했습니다. (쉘 래퍼가 추가되었거나 모두 testname [...]으로 호출 할 수 있고 결과를 확인하기 위해 if를 사용할 수있는 코드가 조정되었습니다)

    결과 : (시간에 따라 측정, 실시간은 가장 가까운 0.5 초로 반올림 됨)

    (grep을 여러 번 호출하는 것은 특히 재귀 적 메서드를 사용하면 예상 한 것보다 낫습니다)

  5. ==============================

    5.재귀 적 솔루션. 하나씩 파일을 반복합니다. 각 파일에 대해 첫 번째 패턴과 일치하는지 확인하고 첫 번째 패턴과 일치하는 경우에만 일찌감치 (첫 번째 일치에서 -m1 :) 두 번째 패턴을 검색하는 등의 작업을 수행합니다.

    재귀 적 솔루션. 하나씩 파일을 반복합니다. 각 파일에 대해 첫 번째 패턴과 일치하는지 확인하고 첫 번째 패턴과 일치하는 경우에만 일찌감치 (첫 번째 일치에서 -m1 :) 두 번째 패턴을 검색하는 등의 작업을 수행합니다.

    #!/bin/bash
    
    patterns="$@"
    
    fileMatchesAllNames () {
      file=$1
      if [[ $# -eq 1 ]]
      then
        echo "$file"
      else
        shift
        pattern=$1
        shift
        grep -m1 -q "$pattern" "$file" && fileMatchesAllNames "$file" $@
      fi
    }
    
    for file in *
    do
      test -f "$file" && fileMatchesAllNames "$file" $patterns
    done
    
    ./allfilter.sh cat filter java
    test.sh
    

    현재 dir에서 토큰 "cat", "filter"및 "java"를 검색합니다. "test.sh"에서만 찾았습니다.

    따라서 grep은 최악의 시나리오에서 자주 호출됩니다 (N 번째 패턴을 제외하고 각 파일의 마지막 줄에서 첫 번째 N-1 패턴을 찾습니다).

    그러나 가능한 한 많은 정보가있는 파일 (가능한 한 초기에 일치하는 것으로 rarly)을 사용하면 많은 파일이 첫 번째 키워드와 일치하지 않기 때문에 일찍 포기되거나 키워드 닫기와 일치하여 일찍 수락되므로 솔루션이 합리적인 속도로 빨라야합니다 정상에.

    예 : tailrec (다소는 거의 사용하지 않음), mutable (거의 사용하지 않지만, 그렇다면 import 문에서 맨 위에), main (거의 사용하지 않고 자주 맨 위에 있지 않음) 및 println 사용, 예측할 수없는 위치), 당신은 그들을 주문할 것입니다 :

    ./allfilter.sh mutable tailrec main println 
    
    ls *.scala | wc 
     89      89    2030
    

    89 개의 스칼라 파일에 키워드 배포가 있습니다.

    for keyword in mutable tailrec main println; do grep -m 1 $keyword *.scala | wc -l ; done 
    16
    34
    41
    71
    

    filepattern을 첫 번째 인수로 사용할 수있는 약간 수정 된 버전의 스크립트로 검색하면 약 0.2 초가 소요됩니다.

    time ./allfilter.sh "*.scala" mutable tailrec main println
    Filepattern: *.scala    Patterns: mutable tailrec main println
    aoc21-2017-12-22_00:16:21.scala
    aoc25.scala
    CondenseString.scala
    Partition.scala
    StringCondense.scala
    
    real    0m0.216s
    user    0m0.024s
    sys 0m0.028s
    

    15.000 코드 라인에 근접 :

    cat *.scala | wc 
      14913   81614  610893
    

    코멘트에 대한 의견을 읽은 후에, 우리는 패턴의 많은 부분에 대해 이야기하고 있을지도 모르며, 논쟁으로 넘겨주는 것이 영리한 생각이 아닌 것 같습니다. 더 나은 파일에서 그들을 읽고, 인수로 파일 이름을 전달 - 어쩌면 너무 필터링하는 파일의 목록 :

    #!/bin/bash
    
    filelist="$1"
    patternfile="$2"
    patterns="$(< $patternfile)"
    
    fileMatchesAllNames () {
      file=$1
      if [[ $# -eq 1 ]]
      then
        echo "$file"
      else
        shift
        pattern=$1
        shift
        grep -m1 -q "$pattern" "$file" && fileMatchesAllNames "$file" $@
      fi
    }
    
    echo -e "Filepattern: $filepattern\tPatterns: $patterns"
    for file in $(< $filelist)
    do
      test -f "$file" && fileMatchesAllNames "$file" $patterns
    done
    

    패턴 / 파일의 수와 길이가 인수 전달의 가능성을 초과하면 패턴 목록을 많은 패턴 파일로 분할하고 루프 (예 : 20 개의 패턴 파일)로 처리 할 수 ​​있습니다.

    for i in {1..20}
    do
       ./allfilter2.sh file.$i.lst pattern.$i.lst > file.$((i+1)).lst
    done
    
  6. ==============================

    6.너는 할 수있다.

    너는 할 수있다.

    데모:

    $ cat input 
    ...
    string1
    ...
    string2
    ...
    string3
    ...
    string1 string2
    ...
    string1 string2 string3
    ...
    string3 string1 string2
    ...
    string2 string3
    ... and so on
    
    $ grep -o -F $'string1\nstring2\nstring3' input|sort -u|wc -l
    3
    
    $ grep -o -F $'string1\nstring3' input|sort -u|wc -l
    2
    
    $ grep -o -F $'string1\nstring2\nfoo' input|sort -u|wc -l
    2
    

    이 솔루션의 한 가지 단점은 부분 일치를 충족시키지 못하면 OK 요구 사항입니다. grep은 겹치는 일치를 감지하지 못합니다. 예를 들어 abcd 텍스트는 abc와 bcd와 일치하지만 grep은 그 중 하나만 찾습니다.

    $ grep -o -F $'abc\nbcd' <<< abcd
    abc
    
    $ grep -o -F $'bcd\nabc' <<< abcd
    abc
    

    이 접근법 / 솔루션은 고정 된 문자열에서만 작동합니다. 단일 정규 표현식이 여러 개의 다른 문자열과 일치 할 수 있고 어떤 정규 표현식이 일치하는지 추적 할 수 없기 때문에 정규 표현식을 위해 확장 할 수 없습니다. 가장 좋은 방법은 임시 파일에 일치 항목을 저장 한 다음 한 번에 하나의 정규식을 사용하여 grep을 여러 번 실행하는 것입니다.

    이 솔루션은 bash 스크립트로 구현되었습니다.

    matchall :

    #!/usr/bin/env bash
    
    if [ $# -lt 2 ]
    then
        echo "Usage: $(basename "$0") input_file string1 [string2 ...]"
        exit 1
    fi
    
    function find_all_matches()
    (
        infile="$1"
        shift
    
        IFS=$'\n'
        newline_separated_list_of_strings="$*"
        grep -o -F "$newline_separated_list_of_strings" "$infile"
    )
    
    string_count=$(($# - 1))
    matched_string_count=$(find_all_matches "$@"|sort -u|wc -l)
    
    if [ "$matched_string_count" -eq "$string_count" ]
    then
        echo "ALL strings matched"
        exit 0
    else
        echo "Some strings DID NOT match"
        exit 1
    fi
    

    데모:

    $ ./matchall
    Usage: matchall input_file string1 [string2 ...]
    
    $ ./matchall input string1 string2 string3
    ALL strings matched
    
    $ ./matchall input string1 string2
    ALL strings matched
    
    $ ./matchall input string1 string2 foo
    Some strings DID NOT match
    
  7. ==============================

    7.파일에 세 가지 패턴이 모두 있는지 확인하는 가장 쉬운 방법은 패턴 만 일치시키고 고유 한 부품과 카운트 라인 만 출력하는 것입니다. 그런 다음 간단한 테스트 조건으로 테스트 할 수 있습니다 : test 3 -eq $ grep_lines.

    파일에 세 가지 패턴이 모두 있는지 확인하는 가장 쉬운 방법은 패턴 만 일치시키고 고유 한 부품과 카운트 라인 만 출력하는 것입니다. 그런 다음 간단한 테스트 조건으로 테스트 할 수 있습니다 : test 3 -eq $ grep_lines.

     grep_lines=$(grep -Eo 'string1|string2|string3' file | uniq | wc -l)
    

    두 번째 질문에 관해서는, 하나 이상의 패턴이 발견되는 즉시 파일 읽기를 중지 할 수 있다고 생각하지 않습니다. grep에 대한 man 페이지를 읽었습니다. 그리고 그걸 도와 줄 수있는 옵션이 없습니다. 일치하는 패턴에 관계없이 grep -m [number] 옵션을 사용하여 특정 행 다음에 행을 읽는 것을 중지 할 수 있습니다.

    그 목적을 위해 커스텀 함수가 필요하다는 것을 확실히 알아라.

  8. ==============================

    8.흥미로운 문제이며, grep man 페이지에 쉬운 대답을 제안하는 것은 명백하지 않습니다. 그것은 할 수 미친 정규식이있을 수 있지만 grep의 직선 체인으로 명확하게 될 수 있습니다, 그 n 번 파일을 스캔 끝나더라도. 적어도 -q 옵션은 매번 첫 번째 매치시 보석을 가지며, 문자열 중 하나가 없으면 && 바로 가기 평가를 수행합니다.

    흥미로운 문제이며, grep man 페이지에 쉬운 대답을 제안하는 것은 명백하지 않습니다. 그것은 할 수 미친 정규식이있을 수 있지만 grep의 직선 체인으로 명확하게 될 수 있습니다, 그 n 번 파일을 스캔 끝나더라도. 적어도 -q 옵션은 매번 첫 번째 매치시 보석을 가지며, 문자열 중 하나가 없으면 && 바로 가기 평가를 수행합니다.

    $grep -Fq string1 t && grep -Fq string2 t && grep -Fq string3 t
    $echo $?
    0
    
    $grep -Fq string1 t && grep -Fq blah t && grep -Fq string3 t
    $echo $?
    1
    
  9. ==============================

    9.아마도 GNU sed로

    아마도 GNU sed로

    고양이 match_word.sh

    sed -z '
      /\b'"$2"'/!bA
      /\b'"$3"'/!bA
      /\b'"$4"'/!bA
      /\b'"$5"'/!bA
      s/.*/0\n/
      q
      :A
      s/.*/1\n/
    ' "$1"
    

    당신은 그것을 다음과 같이 부릅니다 :

    ./match_word.sh infile string1 string2 string3
    

    모든 일치 항목이 발견되면 0을 반환하고 그렇지 않으면 1을 반환합니다.

    여기 네 줄을 찾을 수 있습니다.

    더 원한다면 다음과 같은 행을 추가 할 수 있습니다.

    /\b'"$x"'/!bA
    
  10. ==============================

    10."awk 나 python과 같은 도구를 사용하지 않고도 할 수 있습니까?" 요구 사항은 Perl 스크립트를 사용하여 수행 할 수 있습니다.

    "awk 나 python과 같은 도구를 사용하지 않고도 할 수 있습니까?" 요구 사항은 Perl 스크립트를 사용하여 수행 할 수 있습니다.

    (시스템이나 / bin / env perl과 같은 것을 사용하십시오)

    #!/usr/bin/perl
    
    use Getopt::Std; # option parsing
    
    my %opts;
    my $filename;
    my @patterns;
    getopts('rf:',\%opts); # Allowing -f <filename> and -r to enable regex processing
    
    if ($opts{'f'}) { # if -f is given
        $filename = $opts{'f'};
        @patterns = @ARGV[0 .. $#ARGV]; # Use everything else as patterns
    } else { # Otherwise
        $filename = $ARGV[0]; # First parameter is filename
        @patterns = @ARGV[1 .. $#ARGV]; # Rest is patterns
    }
    my $use_re= $opts{'r'}; # Flag on whether patterns are regex or not
    
    open(INF,'<',$filename) or die("Can't open input file '$filename'");
    
    
    while (my $line = <INF>) {
        my @removal_list = (); # List of stuff that matched that we don't want to check again
        for (my $i=0;$i <= $#patterns;$i++) {
            my $pattern = $patterns[$i];
            if (($use_re&& $line =~ /$pattern/) || # regex match
                (!$use_re&& index($line,$pattern) >= 0)) { # or string search
                push(@removal_list,$i); # Mark to be removed
            }
        }
        # Now remove everything we found this time
        # We need to work backwards to keep us from messing
        # with the list while we're busy
        for (my $i=$#removal_list;$i >= 0;$i--) {
            splice(@patterns,$removal_list[$i],1);
        }
        if (scalar(@patterns) == 0) { # If we don't need to match anything anymore
            close(INF) or warn("Error closing '$filename'");
            exit(0); # We found everything
        }
    }
    # End of file
    
    close(INF) or die("Error closing '$filename'");
    exit(1); # If we reach this, we haven't matched everything
    

    matcher.pl로 저장되어 일반 텍스트 문자열을 검색합니다.

    ./matcher filename string1 string2 string3 'complex string'
    

    그러면 정규 표현식이 검색됩니다.

    ./matcher -r filename regex1 'regex2' 'regex4'
    

    (파일 이름 대신 -f를 사용할 수 있습니다).

    ./matcher -f filename -r string1 string2 string3 'complex string'
    

    단일 라인 매칭 패턴 (파일을 라인 적으로 처리하기 때문에)으로 제한됩니다.

    쉘 스크립트에서 많은 파일을 호출 할 때 성능이 awk보다 느립니다 (그러나 검색 패턴은 -v에서 awk로 공백으로 분리 된 것과는 달리 공백을 포함 할 수 있습니다). 함수로 변환하고 Perl 코드에서 호출하는 경우 (검색 할 파일 목록이 들어있는 파일 포함) 대부분의 awk 구현보다 훨씬 빠릅니다. (작은 파일 몇 개를 호출 할 때 Perl 시작 시간 (스크립트 구문 분석 등)이 시간을 지배합니다)

    정규 표현식의 사용 여부에 관계없이 하드 코딩을 통해 유연성을 확보하여 상당한 속도를 낼 수 있습니다. (Getopt :: Std를 제거하면 어떤 효과가 있는지 보려면 여기를 참고하십시오.)

  11. ==============================

    11.

    perl -lne '%m = (%m, map {$_ => 1} m!\b(string1|string2|string3)\b!g); END { print scalar keys %m == 3 ? "Match": "No Match"}' file
    
  12. ==============================

    12."솔루션 완성도"를 위해서만, 당신은 다른 도구를 사용하여 여러 greps 및 awk / sed 또는 큰 (그리고 아마도 느린) 셸 루프를 피할 수 있습니다. 그러한 도구는 동의합니다.

    "솔루션 완성도"를 위해서만, 당신은 다른 도구를 사용하여 여러 greps 및 awk / sed 또는 큰 (그리고 아마도 느린) 셸 루프를 피할 수 있습니다. 그러한 도구는 동의합니다.

    agrep은 실제로 egrep을 지원하는 패턴이며, 패턴을 사용하고 있습니다. 패턴 구분자로.

    egrep과 마찬가지로 잘 알려진 대부분의 도구와 마찬가지로 agrep는 레코드 / 라인에서 작동하는 도구이므로 전체 파일을 단일 레코드로 처리 할 방법이 필요합니다. 또한 agrep는 사용자 정의 레코드 분리 문자를 설정하는 -d 옵션을 제공합니다.

    일부 테스트 :

    $ cat file6
    str4
    str1
    str2
    str3
    str1 str2
    str1 str2 str3
    str3 str1 str2
    str2 str3
    
    $ agrep -d '$$\n' 'str3;str2;str1;str4' file6;echo $?
    str4
    str1
    str2
    str3
    str1 str2
    str1 str2 str3
    str3 str1 str2
    str2 str3
    0
    
    $ agrep -d '$$\n' 'str3;str2;str1;str4;str5' file6;echo $?
    1
    
    $ agrep -p 'str3;str2;str1' file6  #-p prints lines containing all three patterns in any position
    str1 str2 str3
    str3 str1 str2
    

    완벽한 도구는 없으며 agrep에는 몇 가지 제한 사항이 있습니다. 32 자보다 긴 정규식 / 패턴을 사용할 수 없으며 일부 옵션은 정규식과 함께 사용하면 사용할 수 없습니다.

  13. ==============================

    13.python에서 fileinput 모듈을 사용하면 파일을 명령 행에 지정하거나 텍스트를 stdin의 행별로 읽을 수 있습니다. 문자열을 파이썬 목록에 하드 코딩 할 수 있습니다.

    python에서 fileinput 모듈을 사용하면 파일을 명령 행에 지정하거나 텍스트를 stdin의 행별로 읽을 수 있습니다. 문자열을 파이썬 목록에 하드 코딩 할 수 있습니다.

    # Strings to match, must be valid regular expression patterns
    # or be escaped when compiled into regex below.
    strings = (
        r'string1',
        r'string2',
        r'string3',
    )
    

    다른 파일에서 문자열을 읽거나

    import re
    from fileinput import input, filename, nextfile, isfirstline
    
    for line in input():
        if isfirstline():
            regexs = map(re.compile, strings) # new file, reload all strings
    
        # keep only strings that have not been seen in this file
        regexs = [rx for rx in regexs if not rx.match(line)] 
    
        if not regexs: # found all strings
            print filename()
            nextfile()
    
  14. ==============================

    14.점검 할 모든 문자열이 strings.txt 파일에 있다고 가정하고 체크인 할 파일이 input.txt 인 경우 다음 한 라이너가 수행합니다.

    점검 할 모든 문자열이 strings.txt 파일에 있다고 가정하고 체크인 할 파일이 input.txt 인 경우 다음 한 라이너가 수행합니다.

    의견을 바탕으로 답변을 업데이트했습니다.

    $ diff <( sort -u strings.txt )  <( grep -o -f strings.txt input.txt | sort -u )
    

    설명 :

    grep의 -o 옵션을 사용하여 관심있는 문자열 만 일치시킵니다. 그러면 input.txt 파일에있는 모든 문자열이 제공됩니다. 그런 다음 diff를 사용하여 찾을 수없는 문자열을 가져옵니다. 모든 문자열이 발견되면 결과는 아무 것도 아닙니다. 또는 diff의 종료 코드를 확인하십시오.

    그것이하지 않는 것 :

    그것이하는 일 :

  15. ==============================

    15.이 답변들 중 상당수는 그들이가는 한 괜찮습니다.

    이 답변들 중 상당수는 그들이가는 한 괜찮습니다.

    그러나 성능이 중요한 문제인 경우 - 입력이 크고 패턴이 수천 개가 넘을 경우 가능합니다 - 그렇다면 lex 또는 flex와 같은 도구를 사용하여 큰 속도 향상을 얻습니다.이 도구는 인식기로서 진정한 결정 성있는 유한 오토 마톤을 생성합니다. 패턴 당 한 번씩 정규 표현식 인터프리터를 호출하는 것보다

    유한 오토 마톤은 패턴 수에 관계없이 입력 문자 당 몇 가지 기계 명령어를 실행합니다.

    노 프릴 플렉스 솔루션 :

    %{
    void match(int);
    %}
    %option noyywrap
    
    %%
    
    "abc"       match(0);
    "ABC"       match(1);
    [0-9]+      match(2);
    /* Continue adding regex and exact string patterns... */
    
    [ \t\n]     /* Do nothing with whitespace. */
    .   /* Do nothing with unknown characters. */
    
    %%
    
    // Total number of patterns.
    #define N_PATTERNS 3
    
    int n_matches = 0;
    int counts[10000];
    
    void match(int n) {
      if (counts[n]++ == 0 && ++n_matches == N_PATTERNS) {
        printf("All matched!\n");
        exit(0);
      }
    }
    
    int main(void) {
      yyin = stdin;
      yylex();
      printf("Only matched %d patterns.\n", n_matches);
      return 1;
    }
    

    아래쪽면은 주어진 패턴 세트마다 이것을 만들어야한다는 것입니다. 너무 나쁘지 않습니다.

    flex matcher.y
    gcc -O lex.yy.c -o matcher
    

    이제 실행하십시오 :

    ./matcher < input.txt
    
  16. ==============================

    16.평범한 속도의 경우, 외부 도구 제한이없고 정규식이없는 경우 (C) 버전은 괜찮은 작업입니다. (아마도 리눅스에서만 가능하지만, mmap을 가진 모든 유닉스 계열 시스템에서 작동해야한다)

    평범한 속도의 경우, 외부 도구 제한이없고 정규식이없는 경우 (C) 버전은 괜찮은 작업입니다. (아마도 리눅스에서만 가능하지만, mmap을 가진 모든 유닉스 계열 시스템에서 작동해야한다)

    #include <sys/mman.h>
    #include <sys/stat.h>
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <fcntl.h>
    #include <unistd.h>
    #include <errno.h>
    
    /* https://stackoverflow.com/a/8584708/1837991 */
    inline char *sstrstr(char *haystack, char *needle, size_t length)
    {
        size_t needle_length = strlen(needle);
        size_t i;
        for (i = 0; i < length; i++) {
            if (i + needle_length > length) {
                return NULL;
            }
            if (strncmp(&haystack[i], needle, needle_length) == 0) {
                return &haystack[i];
            }
        }
        return NULL;
    }
    
    int matcher(char * filename, char ** strings, unsigned int str_count)
    {
        int fd;
        struct stat sb;
        char *addr;
        unsigned int i = 0; /* Used to keep us from running of the end of strings into SIGSEGV */
    
        fd = open(filename, O_RDONLY);
        if (fd == -1) {
            fprintf(stderr,"Error '%s' with open on '%s'\n",strerror(errno),filename);
            return 2;
        }
    
        if (fstat(fd, &sb) == -1) {          /* To obtain file size */
            fprintf(stderr,"Error '%s' with fstat on '%s'\n",strerror(errno),filename);
            close(fd);
            return 2;
        }
    
        if (sb.st_size <= 0) { /* zero byte file */
            close(fd);
            return 1; /* 0 byte files don't match anything */
        }
    
        /* mmap the file. */
        addr = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
        if (addr == MAP_FAILED) {
            fprintf(stderr,"Error '%s' with mmap on '%s'\n",strerror(errno),filename);
            close(fd);
            return 2;
        }
    
        while (i++ < str_count) {
            char * found = sstrstr(addr,strings[0],sb.st_size);
            if (found == NULL) {  /* If we haven't found this string, we can't find all of them */
                munmap(addr, sb.st_size);
                close(fd);
                return 1; /* so give the user an error */
            }
            strings++;
        }
        munmap(addr, sb.st_size);
        close(fd);
        return 0; /* if we get here, we found everything */
    }
    
    int main(int argc, char *argv[])
    {
        char *filename;
        char **strings;
        unsigned int str_count;
        if (argc < 3) { /* Lets count parameters at least... */
            fprintf(stderr,"%i is not enough parameters!\n",argc);
            return 2;
        }
        filename = argv[1]; /* First parameter is filename */
        strings = argv + 2; /* Search strings start from 3rd parameter */
        str_count = argc - 2; /* strings are two ($0 and filename) less than argc */
    
        return matcher(filename,strings,str_count);
    }
    

    다음과 같이 컴파일하십시오.

    gcc matcher.c -o matcher
    

    다음과 같이 실행하십시오.

    ./matcher filename needle1 needle2 needle3
    

    크레딧 :

    노트:

  17. ==============================

    17.다음 python 스크립트는 트릭을 수행해야합니다. 종류에 따라 grep (re.search)에 해당하는 함수가 여러 줄씩 호출됩니다. 즉, 각 줄마다 각 패턴을 검색하지만, 매번 프로세스를 포킹하지 않으므로 훨씬 효율적입니다. 또한, 이미 발견 된 패턴을 제거하고 모든 패턴이 발견되면 중지합니다.

    다음 python 스크립트는 트릭을 수행해야합니다. 종류에 따라 grep (re.search)에 해당하는 함수가 여러 줄씩 호출됩니다. 즉, 각 줄마다 각 패턴을 검색하지만, 매번 프로세스를 포킹하지 않으므로 훨씬 효율적입니다. 또한, 이미 발견 된 패턴을 제거하고 모든 패턴이 발견되면 중지합니다.

    #!/usr/bin/env python
    
    import re
    
    # the file to search
    filename = '/path/to/your/file.txt'
    
    # list of patterns -- can be read from a file or command line 
    # depending on the count
    patterns = [r'py.*$', r'\s+open\s+', r'^import\s+']
    patterns = map(re.compile, patterns)
    
    with open(filename) as f:
        for line in f:
            # search for pattern matches
            results = map(lambda x: x.search(line), patterns)
    
            # remove the patterns that did match
            results = zip(results, patterns)
            results = filter(lambda x: x[0] == None, results)
            patterns = map(lambda x: x[1], results)
    
            # stop if no more patterns are left
            if len(patterns) == 0:
                break
    
    # print the patterns which were not found
    for p in patterns:
        print p.pattern
    

    일반 문자열 (정규식이 아닌 문자열)을 처리하는 경우 일반 문자열 (줄의 문자열)에 대해 별도의 검사를 추가 할 수 있습니다. 이는 약간 더 효율적입니다.

    그게 당신의 문제를 해결합니까?

  18. ==============================

    18.나는 답변들 사이에 간단한 카운터를 보지 못했기 때문에 모든 일치가 만족되는 즉시 중지하는 awk를 사용하는 카운터 지향 솔루션이 여기에 있습니다.

    나는 답변들 사이에 간단한 카운터를 보지 못했기 때문에 모든 일치가 만족되는 즉시 중지하는 awk를 사용하는 카운터 지향 솔루션이 여기에 있습니다.

    /string1/ { a = 1 }
    /string2/ { b = 1 }
    /string3/ { c = 1 }
    {
        if (c + a + b == 3) {
            print "Found!";
            exit;
        }
    }
    

    쉘 인자를 통해 사용법을 확장하기 :

    #! /bin/sh
    awk -v vars="$*" -v argc=$# '
    BEGIN { split(vars, args); }
    {
        for (arg in args) {
            if (!temp[arg] && $0 ~ args[arg]) {
                inc++;
                temp[arg] = 1;
            }
        }
    
        if (inc == argc) {
            print "Found!";
            exit;
        }
    }
    END { exit 1; }
    ' filename
    

    사용법 (정규 표현식을 전달할 수 있음) :

    ./script "str1?" "(wo)?men" str3
    

    또는 일련의 패턴을 적용 할 수 있습니다.

    ./script "str1? (wo)?men str3"
    
  19. ==============================

    19.

    $ cat allstringsfile | tr '\n' ' ' |  awk -f awkpattern1
    

    여기서 allstringsfile은 원본 질문과 마찬가지로 텍스트 파일입니다. awkpattern1은 문자열 패턴을 포함하며 && 조건을 포함합니다.

    $ cat awkpattern1
    /string1/ && /string2/ && /string3/
    
  20. from https://stackoverflow.com/questions/49762772/check-if-all-of-multiple-strings-or-regexes-exist-in-a-file by cc-by-sa and MIT license