다음과 같은 한 줄 파일이 있습니다.파스퇴르 빈그러나 훨씬 더 길다.
내 목표는 example1: start with <a
end with </a>
example2: start with PZ
end with 의 문자열 부분 만 필터링하는 것입니다 s16
. 따라서 각 경우 텍스트는 HTML 엔터티에 의존하지 않고 일치 항목 사이에 유지됩니다.
FreeBSD
HTML 엔터티에 의존하는 해결 방법이 이미 있습니다.
- 여러 줄로 아름답게 꾸미기
tidy -i -m -w 160 -ashtml -utf8 ~/file
- 문자열이 포함되어 있지 않으면 행 삭제
sed -i '' '/\<\/a\>/!d' ~/file
그런데 HTML 엔터티에 의존하지 않고 직접 필터를 실행하려고 합니다. 현재는 일치의 정확한 시작 부분만 얻을 수 있지만 필터링하고 있는 문자열 내용이 얼마나 긴지 모르기 때문에 일치의 끝 부분을 정확히 얻을 수 없습니다. 예상치 못한 결과 재현 단계를 참조하세요.
예상치 못한 결과를 재현하는 단계
wget -O ~/file https://pastebin.com/raw/xbti369J
grep -E -o ".{0,0}PZ.{0,46}" ~/file
고정된 길이를 요청했기 때문에 행이 잘못되었습니다.
PZ</td><td class="s15">€ 1.20</td><td class="s16
PZ</td><td class="s15"></td><td class="s16">A</t
목표는 아래와 같이 길이에 관계없이 결과적인 선 패턴을 얻는 것입니다.
PZ</td><td class="s15">€ 1.20</td><td class="s16
PZ</td><td class="s15"></td><td class="s16
답변1
와 같은 XML 파서를 사용하고 싶습니다 xmllint
.
a
다음 XPath 표현식을 사용하여 요소 사이의 텍스트를 필터링합니다 .
xmllint --html --xpath '//a/text()' <file>
답변2
PZ
a 에서 가장 가까운 것까지 모든 조각을 선택하려면 s16
탐욕스럽지 않은 일치가 필요합니다. 이는 (확장) 정규 표현식에서 지원되지 않지만 grep
GNU에는 grep
Perl 스타일 표현식에 대한 erl 옵션이 있습니다.-P
grep -P -o "PZ.*?s16" ~/file
Perl 표현식 ".*?"는 전체 표현식을 일치시키는 문자 중 가장 짧은 일치를 나타냅니다.
PZ
경기 내부에 더 많은 것이 있기 때문에 이것은 여전히 당신이 원하는 것이 아닐 수도 있지만 , 당신의 예를 이해하면 PZ
뒤에 있는 것만 필요하고 s16
그 사이에는 아무것도 필요하지 않습니다. PZ
이제 두 번째 단계에서 불필요한 항목을 제거해 보겠습니다.
grep -P -o "PZ.*?s16" ~/file | sed 's/.*PZ/PZ/'
답변3
이를 수행하는 방법에는 여러 가지가 있습니다.
1PCRE가 활성화된 GNU grep. 여기서는 탐욕스럽지 않은 정규식 *?를 부정적인 예측과 함께 활용하여 PZ와 s16 사이에 발생하는 모든 PZ를 삭제합니다.
grep -Po 'PZ(?:(?!PZ).)*?s16' file
2 그러한 grep 버전에 액세스할 수 없는 경우 정규식을 지원하는 원래 버전인 Perl을 사용할 수 있습니다.
perl -lne 'print for /PZ(?:(?!PZ).)*?s16/g' file
삼 이를 위해 sed를 사용할 수 있습니다. 첫 번째 단계에서는 PZ 및 s16을 BOL 및 EOL로 표시합니다. 이 수정된 입력은 PZ로 시작하고 s16으로 끝나는 줄을 선택하는 두 번째 sed로 전달되며 내부적으로 PZ를 포함해서는 안 됩니다.
< file \
sed 's/PZ/\n&/g;s/s16/&\n/g' |
sed '/^PZ.*s16$/!d;/..*PZ/d' |
cat
4 우리를. 여기서는 단 하나의 sed 호출만 사용됩니다. GNU sed가 필요합니다.
sed '/\n/{
/^PZ[^\n]*s16/!D
s//&\n/;P;D;}
s/PZ/\n&/g;D
' file