다음 줄에 패턴 2가 포함되어 있지 않으면 패턴 1과 일치하는 줄을 인쇄합니다.

다음 줄에 패턴 2가 포함되어 있지 않으면 패턴 1과 일치하는 줄을 인쇄합니다.

다음 줄에 bar가 포함되어 있지 않으면 foo가 포함된 줄과 일치시키고 싶습니다. 따라서 다음을 포함하는 파일이 제공됩니다.

1 foo 1 
foo 2
baz bar bap

인쇄 만 됩니다 1 foo 1. 나는 /foo(?!.*\n.*bar)/그것이 작동하도록 부정적인 예측을 사용했습니다.https://regex101.com/r/ZMZsiN/1그러나 grep과 perl을 사용하여 명령줄에서 사용하는 것은 모두 실패했습니다. Perl, sed, awk 또는 Python에서 grep 또는 한 줄 문을 사용하는 모든 솔루션은 훌륭할 것입니다. Chatgpt가 실패했습니다.

시도해 볼 사항:

$grep -Pwe 'foo(?!.*\n.*bar)' testfile
1 foo 1 
foo 2
$perl -wnl -e  /'foo(?!\n.*bar)/ and print' testfile
1 foo 1 
foo 2
$perl -ne 'print if /foo/ && ($_ = <>) !~ /bar/' testfile
foo 2

마지막 것은 chatgpt가 제공하는 것을 기반으로 하고 가깝지만 내 perlfu는 아직 문제를 파악하기에 충분하지 않습니다.

답변1

grep또는 perl -n한 번에 한 줄만 처리하므로 정규 표현식은 한 줄의 내용만 일치합니다(줄 구분 기호는 grep또는 perl에 포함되지 않습니다 -l).

여러 pcregrep줄 모드 와 .-Pgrep-M

pcregrep -M '\bfoo\b(?!.*\n.*\bbar\b)'

필요에 따라 일치하는 항목에 더 많은 줄을 가져오는 것 외에도 여러 줄 모드는 항목 의 시작과 끝 뿐만 아니라 모든 줄의 시작과 끝 에서 일치하는 플래그(암시적으로 )를 pcregrep활성화합니다 .m(?m)^$아니요이 플래그를 활성화하면 개행 문자가 일치하지 않는다는 s의미입니다 ..

( \b단어 b경계, -w유용한 곳에 단어 경계를 두지 않습니다).

를 사용하면 perl -n정규 표현식이 전체 파일에서 일치하는 것이 불가능한 것으로 레코드 구분 기호를 설정할 수 있습니다.

perl -0777 -ne '
  print for m{^.*\bfoo\b.*\n(?!.*\bbar\b)}mg'

표준 Unix 도구 상자를 사용하면 가능 sed하지만 표준에는 sed단어 경계 연산자가 없으므로 서투른 해결 방법이 필요합니다.

sed -n '/^\(.*[^[:alnum:]_]\)\{0,1\}foo\([^[:alnum:]_].*\)\{0,1\}$/ {
          $!N
          /\n\(.*[^[:alnum:]_]\)\{0,1\}bar\(.*[^[:alnum:]_]\)\{0,1\}.*$/!P
          D
        }'

답변2

그리고 grep:

(정규 표현식이 향상되었습니다.단어 경계)

grep -Pzo '(?m)\bfoo\b(?!.*\n.*\bbar\b)' file
foo
  • -P모델
  • -zNULL \0모든 행을 문자열로 구문 분석할 수 있도록 구분됩니다 ( \0의미파일 이름 끝)
  • -o일치하는 부분만 표시되도록 설계됨

정규식 일치는 다음과 같습니다.

마디 설명하다
(?m) 이 블록에 대한 플래그 설정(^ 및 $는 줄의 시작 및 끝과 일치)(대소문자 구분)(.는 \n과 일치하지 않음)(보통 공백 및 #과 일치)
\b 단어 문자(\w)와 단어가 아닌 문자 사이의 경계
foo '부자'
\b 단어 문자(\w)와 단어가 아닌 문자 사이의 경계
(?! 시야다음이 있는지 확인하세요.
.* 모든 문자(0회 이상(최대한 많이 일치))
\n '\n'(개행 문자)
.* 모든 문자(0회 이상(최대한 많이 일치))
\b 단어 문자(\w)와 단어가 아닌 문자 사이의 경계
bar '술집'
\b 단어 문자(\w)와 단어가 아닌 문자 사이의 경계
) 미리보기 끝

그리고php(regex101은 정규식 스타일을 기반으로 한 것 같습니다 PCRE):

php -r '$pattern = "1 foo 1 
foo 2
baz bar bap";
preg_match("/foo(?!.*\n.*bar)/m", $pattern, $matches, PREG_OFFSET_CAPTURE);
print_r($matches);'

산출

Array
(
    [0] => Array
        (
            [0] => foo
            [1] => 2
        )

)

인쇄하려면 foo다음을 사용하세요.echo $matches[0][0];

답변3

인생에서와 마찬가지로 소프트웨어에서도 원하는 대로 행동하는 것이 훨씬 쉽습니다.가지다근거가 아닌 과거에 일어난 일(예: 읽은 데이터)~ 할 것이다미래에 발생합니다(즉, 아직 읽지 않은 데이터).

awk를 사용하십시오.

$ awk '(p ~ /foo/) && !/bar/{print p} {p=$0} END{if (p ~ /foo/) print p}' file
1 foo 1

현재 줄에 foo가 포함되어 있고 다음 줄에 bar가 포함되어 있지 않으면 현재 줄을 인쇄하려고 시도하지 않습니다. 그러나 이전 줄에 foo가 포함되어 있고 현재 줄에 bar가 포함되어 있지 않으면 이전 줄은 인쇄되지 않습니다.

관련 정보