다음 줄에 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
줄 모드 와 .-P
grep
-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
예폴리 메라 제 연쇠 반응모델-z
NULL
\0
모든 행을 문자열로 구문 분석할 수 있도록 구분됩니다 (\0
의미파일 이름 끝)-o
일치하는 부분만 표시되도록 설계됨
정규식 일치는 다음과 같습니다.
마디 | 설명하다 |
---|---|
(?m) |
이 블록에 대한 플래그 설정(^ 및 $는 줄의 시작 및 끝과 일치)(대소문자 구분)(.는 \n과 일치하지 않음)(보통 공백 및 #과 일치) |
\b |
단어 문자(\w)와 단어가 아닌 문자 사이의 경계 |
foo |
'부자' |
\b |
단어 문자(\w)와 단어가 아닌 문자 사이의 경계 |
(?! |
시야다음이 있는지 확인하세요. |
.* |
모든 문자(0회 이상(최대한 많이 일치)) |
\n |
'\n'(개행 문자) |
.* |
모든 문자(0회 이상(최대한 많이 일치)) |
\b |
단어 문자(\w)와 단어가 아닌 문자 사이의 경계 |
bar |
'술집' |
\b |
단어 문자(\w)와 단어가 아닌 문자 사이의 경계 |
) |
미리보기 끝 |
그리고PHPphp
(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가 포함되어 있지 않으면 이전 줄은 인쇄되지 않습니다.