답변:
이 작업에는 Grep이 충분하지 않습니다.
대부분의 최신 Linux 시스템에서 발견되는 pcregrep 은 다음과 같이 사용할 수 있습니다.
pcregrep -M 'abc.*(\n|.)*efg' test.txt
여기서 -M
, --multiline
패턴이 한 줄보다 더 일치시킬 수 있습니다
최신 pcre2grep 도 있습니다. 둘 다 PCRE 프로젝트에 의해 제공됩니다 .
pcre2grep는 포트 의 일부로 Mac 포트 를 통해 Mac OS X에서 사용할 수 있습니다 pcre2
.
% sudo port install pcre2
다음과 같이 Homebrew 를 통해 :
% brew install pcre
또는 pcre2
% brew install pcre2
pcre2grep는 Linux (Ubuntu 18.04+) 에서도 사용 가능 합니다
$ sudo apt install pcre2-utils # PCRE2
$ sudo apt install pcregrep # Older PCRE
-M, --multiline
패턴이 두 줄 이상 일치하도록 허용합니다.
'abc.*(\n|.)*?efg'
.*
> - 'abc(\n|.)*?efg'
정규 표현식 단축하기 위해 (그리고 현학적 수)
grep이 가능한지 확실하지 않지만 sed는 매우 쉽습니다.
sed -e '/abc/,/efg/!d' [file-with-content]
sed
하지만 이전에 그런 표현을 본 적이 없다면.
이 답변에서 영감을 얻은 솔루션은 다음과 같습니다 .
'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
출력이 정상적으로 인쇄 될 각 입력 파일의 인쇄 이름.
(?s)
PCRE_DOTALL을 활성화하십시오. 즉, '.' 모든 문자 또는 개행을 찾습니다.
l
. AFAIK 번호 -1
옵션 이 없습니다 .
-z
옵션이 개행을 처리하기 위해 grep을 지정 하면 zero byte characters
왜 (?s)
정규 표현식에 개행이 필요 합니까? 이미 개행 문자가 아닌 .
경우 직접 일치시킬 수 없습니까?
pcregrep에 크게 의존했지만 최신 grep을 사용하면 많은 기능을 위해 pcregrep을 설치할 필요가 없습니다. 그냥 사용하십시오 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)'은 'newline'이외의 다른 것 또는 'newline'과 일치한다는 것을 의미합니다.
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에서 온 것입니다.
tr
개행을 다른 문자로 바꾸려면 먼저 사용하여 쉽게 수행 할 수 있습니다 .
tr '\n' '\a' | grep -o 'abc.*def' | tr '\a' '\n'
여기서는 \a
개행 문자 대신 알람 문자 (ASCII 7)를 사용하고 있습니다. 이것은 거의 텍스트에서 찾을 수 없으며로 grep
일치 시키 .
거나 구체적으로 일치 시킬 수 있습니다 \a
.
\0
하고 있었고 필요 grep -a
하고 일치했습니다 \x00
... 당신은 나를 단순화하는 데 도움이되었습니다! echo $log | tr '\n' '\0' | grep -aoE "Error: .*?\x00Installing .*? has failed\!" | tr '\0' '\n'
은 지금echo $log | tr '\n' '\a' | grep -oE "Error: .*?\aInstalling .*? has failed\!" | tr '\a' '\n'
grep -o
.
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으로 어떻게 할 것인지 모르겠지만 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 에 대한 자세한 정보를 찾을 수 있습니다 .
sift -lm 'abc.*efg' testfile
효과 가 없다고 생각 합니다. 왜냐하면 일치하는 것은 탐욕스럽고 efg
파일 의 마지막까지 모든 줄을 모으기 때문 입니다.
패턴 순서에 관심이없는 경우 grep을 사용할 수 있습니다.
grep -l "pattern1" filepattern*.* | xargs grep "pattern2"
예
grep -l "vector" *.cpp | xargs grep "map"
grep -l
첫 번째 패턴과 일치하는 모든 파일을 찾고 xargs는 두 번째 패턴에 대해 grep합니다. 도움이 되었기를 바랍니다.
Balu 모한의 대답에 대한 대안으로,이 패턴의 순서 만 사용하여 적용 할 수 있습니다 grep
, 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"
, 또는 어디에 둘 다 같은 줄에 표시를 :
$ 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
- i
일을 포함 하여 모든 줄을 인쇄하십시오.grep -n
-줄 번호와 일치하는 줄 앞에 추가head -n1
-첫 번째 행만 인쇄cut -d : -f 1
- :
구분자로 사용하여 첫 번째 절단 열을 인쇄2>/dev/null
- 표현식이 비어있는 tail
경우 발생하는 무음 오류 출력$()
grep -q
- grep
종료 코드에만 관심이 있기 때문에 일치하는 것이 발견되면 즉시 침묵 하고 반환&>
시겠습니까? 나는 그것을 사용하고 있지만 어디서나 문서화 된 것을 본 적이 없다. BTW, 왜 그런 식으로 grep을 침묵시켜야합니까? grep -q
트릭도하지 않습니까?
&>
bash가 표준 출력과 표준 오류를 리디렉션하도록 지시합니다. bash 매뉴얼의 REDIRECTION을 참조하십시오. 당신은 우리가 잘 잡을 수있는 grep -q ...
대신에 할 수 있다는 점에서 매우 옳습니다 grep ... &>/dev/null
!
파일 패턴 *.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의 마지막 일치 (모두 찾기 및 tail -n 1로 마지막 일치)를 찾으십시오.
z=$( grep -n efg $f | tail -n 1)
그렇지 않으면 계속하십시오.
결과는 18:foofile.sh String alf="abc";
":"에서 줄 끝까지 잘라 내야합니다.
((${z/:*/}-${a/:*/}))
두 번째 표현식의 마지막 일치가 첫 번째 일치의 첫 번째 일치를 지나면 양수 결과를 반환해야합니다.
그런 다음 filename을보고합니다 echo $f
.
왜 간단하지 않은가?
egrep -o 'abc|efg' $file | grep -A1 abc | grep efg | wc -l
0 또는 양의 정수를 반환합니다.
egrep -o (일치 만 표시, 트릭 : 동일한 줄의 여러 일치 항목이 다른 줄에있는 것처럼 여러 줄 출력을 생성 함)
grep -A1 abc
(ABC와 그 뒤에 줄을 인쇄하십시오)
grep efg | wc -l
(동일한 또는 다음 줄에서 abc 이후에 발견 된 0-n 개의 efg 줄, 결과는 'if'에 사용될 수 있습니다)
패턴 일치가 필요한 경우 grep을 egrep 등으로 변경할 수 있습니다.
이것은 작동해야합니다 :
cat FILE | egrep 'abc|efg'
일치하는 항목이 둘 이상인 경우 grep -v를 사용하여 필터링 할 수 있습니다.