두 단어 중 하나만 포함하는 줄을 어떻게 grep합니까?


25

grep두 단어 중 하나만 포함하면 두 단어 중 하나를 포함하는 행만 표시 하려고 하지만 동일한 줄에있는 경우에는 사용하지 않습니다.

지금까지 시도 grep pattern1 | grep pattern2 | ...했지만 예상 한 결과를 얻지 못했습니다.


(1)“단어”와“패턴”에 대해 이야기합니다. 무엇 이니? “quick”,“brown”및“fox”와 같은 일반적인 단어 또는 [a-z][a-z0-9]\(,7\}\(\.[a-z0-9]\{,3\}\)+? (2) 단어 / 패턴 중 하나가 한 줄에 두 번 이상 나타나고 다른 하나는 나타나지 않으면 어떻게해야합니까? 이 단어가 한 번 나타나는 단어와 동일합니까, 아니면 여러 번 나타나는 것으로 간주됩니까?
G-Man, 'Reinstate Monica'1

답변:


59

grep갈 길 이외의 도구 입니다.

예를 들어 perl을 사용하면 명령은 다음과 같습니다.

perl -ne 'print if /pattern1/ xor /pattern2/'

perl -nestdin의 각 줄에 주어진 명령을 실행합니다.이 경우 줄이 일치 /pattern1/ xor /pattern2/하거나 다른 패턴이 아닌 다른 패턴과 일치하지 않으면 (인쇄 또는).

이것은 어느 순서로든 패턴에 대해 작동하며 여러 번의 호출보다 성능이 좋으며 grep입력도 적습니다.

또는 awk와 함께 더 짧습니다.

awk 'xor(/pattern1/,/pattern2/)'

또는없는 awk 버전 xor:

awk '/pattern1/+/pattern2/==1`

4
니스-Awk xor는 GNU Awk에서만 사용할 수 있습니까?
스틸 드라이버

9
@steeldriver 그래야만 GNU라고 생각합니다. 또는 이전 버전에서는 누락되었습니다. /pattern1/+/pattern2/==1ir xor가없는 것으로 교체 할 수 있습니다 .
크리스

4
@JimL. \b패턴 자체에 단어 경계 ( )를 넣을 수 있습니다 ( 예 :) \bword\b.
wjandrea

4
@vikingsteve grep을 구체적으로 사용하려면 여기에 다른 답변이 많이 있습니다. 그러나 일을 끝내고 싶은 사람들에게는 grep이하는 모든 일을 할 수있는 도구가 더 쉽고 더 있다는 것을 아는 것이 좋습니다.
크리스

3
@vikingsteve 저는 grep 솔루션에 대한 수요가 일종의 XY 문제라고 강하게 생각합니다
Hagen von Eitzen

30

GNU grep를 사용하면 두 단어를 모두 전달한 grep다음 두 패턴을 모두 포함하는 행을 제거 할 수 있습니다.

$ cat testfile.txt
abc
def
abc def
abc 123 def
1234
5678
1234 def abc
def abc

$ grep -w -e 'abc' -e 'def' testfile.txt | grep -v -e 'abc.*def' -e 'def.*abc'
abc
def

16

함께 시도 egrep

egrep  'pattern1|pattern2' file | grep -v -e 'pattern1.*pattern2' -e 'pattern2.*pattern1'

3
도 같이 쓸 수있다grep -e foo -e bar | grep -v -e 'foo.*bar' -e 'bar.*foo'
글렌 잭맨

8
또한, 그렙 사람 페이지에서 참고 사항 : Direct invocation as either egrep or fgrep is deprecated- 선호grep -E
글렌 잭맨

그것은 내 OS @glennjackman에 없습니다
Grump

1
@Grump 정말? 그게 무슨 OS입니까? 심지어 POSIX는 언급 그렙 가지고해야 -f하고 -e기존의 옵션 비록 egrep그리고 fgrep잠시 동안 계속 지원됩니다.
terdon

1
@terdon, POSIX는 POSIX 유틸리티의 경로를 지정하지 않습니다. 다시 말하지만,이 표준 grep(즉 지원 -F,-E , -e, -fPOSIX의 요구에 따라)입니다 /usr/xpg4/bin. 유틸리티 /bin는 구식입니다.
Stéphane Chazelas

12

grepPerl과 같은 정규 표현식 ( pcregrep또는 GNU 또는 ast-open 등 grep -P) 을 지원하는 구현을 사용하면 다음을 사용 하여 한 번의 grep호출로 수행 할 수 있습니다 .

grep -P '^(?=.*pat1)(?!.*pat2)|^(?=.*pat2)(?!.*pat1)'

즉, 라인 일치하는 찾을 수있다 pat1하지만 pat2, 또는 pat2아니지만을 pat1.

(?=...)그리고 (?!...)각각 미리보고 부정적인 모습 앞서 사업자한다. 그래서 기술적으로, 대상 (의 시작 부분에 대해 위의 모습은 ^)는 다음 있어요 제공 .*pat1하고 다음에하지 .*pat2, 또는와 동일 pat1pat2반대.

두 패턴을 두 번 찾을 때 두 패턴을 모두 포함하는 라인에는 적합하지 않습니다. 대신 다음과 같은 고급 펄 연산자를 사용할 수 있습니다.

grep -P '^(?=.*pat1|())(?(1)(?=.*pat2)|(?!.*pat2))'

(?(1)yespattern|nopattern) 에 대한 일치 yespattern1st 캡처 그룹 ( ()위의 빈 )이 일치 하는지 여부nopattern 합니다. 이 경우 ()일치 수단이 있음을 pat1우리는 찾아 일치하지 않는, 그래서 pat2(앞서 긍정적 인 모양), 우리는 찾아 하지 pat2 , 그렇지 않으면 (음수 봐 앞서를).

로 다음과 같이 sed작성할 수 있습니다.

sed -ne '/pat1/{/pat2/!p;d;}' -e '/pat2/p'

첫 번째 솔루션 grep: the -P option only supports a single pattern은 적어도 내가 액세스 할 수있는 모든 시스템에서 실패 합니다. 그러나 두 번째 솔루션은 +1입니다.
크리스

1
@Chris, 네 말이 맞아. 그것은 GNU에 한정된 것 같습니다 grep. pcregrepast-open grep에는 그런 문제가 없습니다. 나는 배수 -e를 대체 RE 연산자로 대체 했으므로 grep이제 GNU 에서도 작동해야합니다 .
Stéphane Chazelas

예, 지금은 잘 작동합니다.
크리스

3

부울 용어로 A xor B를 찾고 있습니다.

(B가 아닌 A)

또는

(A가 아닌 B)

귀하의 질문에 일치하는 줄이 표시되는 한 출력 순서에 관심이 있다고 언급하지 않는다면 A xor B의 부울 확장은 grep에서 매우 간단합니다.

$ cat << EOF > foo
> a b
> a
> b
> c a
> c b
> b a
> b c
> EOF
$ grep -w 'a' foo | grep -vw 'b'; grep -w 'b' foo | grep -vw 'a';
a
c a
b
c b
b c

1
이것은 작동하지만 파일 순서를 뒤섞습니다.
Sparhawk

@Sparhawk True, "스크램블"은 가혹한 단어입니다. ;) 먼저 모든 'a'일치 항목을 순서대로 나열한 다음 모든 'b'일치 항목을 순서대로 나열합니다. OP는 주문 유지에 관심을 나타내지 않았으며 라인을 보여줍니다. FAWK, 다음 단계는입니다 sort | uniq.
Jim L.

공정한 전화; 언어가 정확하지 않다는 데 동의합니다. 원래 주문이 변경 될 것이라는 것을 의미했습니다.
Sparhawk

1
@Sparhawk ... 그리고 전체 공개를 위해 귀하의 관찰을 편집했습니다.
Jim L.

-2

다음 예의 경우 :

# Patterns:
#    apple
#    pear

# Example line
line="a_apple_apple_pear_a"

이것은 순전히 수행 할 수 있습니다 grep -E, uniq하고 wc.

# Grep for regex pattern, sort as unique, and count the number of lines
result=$(grep -oE 'apple|pear' <<< $line | sort -u | wc -l)

grepPerl 정규식으로 컴파일 된 경우 다음으로 파이프하는 대신 마지막 항목에서 일치시킬 수 있습니다 uniq.

# Grep for regex pattern and count the number of lines
result=$(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l)

결과를 출력하십시오.

# Only one of the words exists if the result is < 2
((result > 0)) &&
   if (($result < 2)); then
      echo Only one word matched
   else
      echo Both words matched
   fi

원 라이너 :

(($(grep -oP '(apple(?!.*apple)|pear(?!.*pear))' <<< $line | wc -l) == 1)) && echo Only one word matched

패턴을 하드 코딩하지 않으려면 다양한 요소 세트로 패턴을 조합하여 기능을 사용하여 자동화 할 수 있습니다.

이것은 파이프 또는 추가 프로세스가없는 함수로 Bash에서 기본적으로 수행 될 수 있지만 더 복잡하고 아마도 귀하의 질문 범위를 벗어납니다.


(1) 누군가 Perl 정규식을 사용하여 답변을 할 때 궁금합니다. 게시물의 해당 부분에 초점을 맞추고 어떻게 작동하는지 설명했다면 이것은 좋은 대답이 될 수 있습니다. (2) 그러나 나는 나머지가 그렇게 좋지 않다는 것을 두려워한다. 문제는 " 두 단어 중 하나를 포함하는 줄만 표시"라고 강조합니다 (강조 추가). 출력 있어야 할 경우 라인 , 다음은 입력이 여러 있어야한다는 이유에 서 선.   그러나 접근 방식은 한 줄만 볼 때만 작동 합니다 . … (계속)
G-Man, 'Reinstate

(계속)… 예를 들어, 입력에 행 Big apple\n과 가 포함되어 있으면 pear-shaped\n출력에 두 줄이 모두 포함되어야합니다. 귀하의 솔루션은 2의 수를 얻습니다. 긴 버전은 "모두 일치하는 단어"(잘못된 질문에 대한 답변)를보고하고 짧은 버전은 아무 것도 말하지 않습니다. (3) 제안 : -o일치하는 내용이 포함 된 줄을 숨기므로 여기에 사용하는 것은 정말 나쁜 생각이므로 두 단어가 같은 줄에 나타나는 경우를 볼 수 없습니다. … (계속)
G-Man, 'Reinstate

(계속)… (4) 결론 : 각 줄의 마지막 항목에만 일치하는 uniq/ sort -u및 멋진 Perl 정규식을 사용한다고해서 실제로이 질문에 유용한 답이되는 것은 아닙니다. 그러나 그들이 그렇게해도 질문에 대답하는 데 어떻게 도움이 되는지 설명하지 않기 때문에 여전히 나쁜 대답이 될 것 입니다. ( 좋은 설명의 예는 Stéphane Chazelas의 답변 을 참조하십시오 .)
G-Man은

OP는 "두 단어 중 하나를 포함하는 행만 표시"하고 싶었다고 말합니다. 즉, 각 행은 자체적으로 평가되어야합니다. 왜 이것이 질문에 대답하지 않는다고 생각하는지 모르겠습니다. 실패 할 것 같은 입력 예를 제공하십시오.
Zhro

아, 그게 무슨 뜻이야? “한 번에 한 줄씩 입력을 읽고 모든 줄에 대해이 두세 개의 명령 실행하십시오 . "? (1) 그것이 당신이 의미 한 바는 고통 스럽습니다. (2) 고통스럽게 비효율적입니다. 네 가지 대답 은 몇 가지 명령 (1, 2 또는 4)으로 전체 파일 을 처리하는 방법을 보여 주었고 n 줄의 입력에 대해 3 × n 명령 을 실행하고 싶  습니까? 작동하더라도 불필요하게 비싼 실행에 대해서는 투표권을 얻습니다. (3) 머리카락이 갈라질 위험이 있지만 여전히 적절한 선 을 표시 하지는 않습니다 .
G-Man,
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.