명령줄에서 많은 양의 텍스트를 구문 분석하고 모든(중첩된) 텍스트 따옴표를 공백으로 바꿔야 합니다. 따옴표는 특정 구문으로 표시됩니다: [quote=username]quoted text[/quote]
.
중첩된 따옴표가 있는 입력 예는 다음과 같습니다.
text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3
예상되는 출력은 다음과 같습니다.
text part 1 text part 2 text part 3
의 도움으로이 문제어떻게든 작동하게 했지만(위의 출력을 얻었습니다) sed ':b; s/\[quote=[^]]*\][^[\/]*\[\/quote\]/ /g; t b'
중간 부분( ]에는 따옴표에 또는 같은 문자가 [^[\/]
포함될 수 있기 때문에 문제가 있습니다 .[
]
즉, sed
입력이 예를 들어 다음과 같은 경우 내 명령이 작동하지 않습니다.
text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [foo] [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3
한 가지 문제는 sed
탐욕스럽지 않은 한정자를 지원하지 않는 것 같아서 가능한 가장 긴 일치 항목이 항상 입력에서 캡처된다는 것입니다. 다루기가 어렵습니다ㅏ)사용자 이름 및비)일반 인용문.
나는 또한 이것이 sed
이 문제에 대한 최선의 도구가 아니며 그런 일을 수행하지 못할 수도 있다고 생각합니다. 예를 들어 아마도. perl
아니면 awk
더 잘 작동할 수 있을까요?
이제 마지막 질문은 이 문제를 해결하는 가장 좋고 효율적인 방법은 무엇입니까?
답변1
입력에 <
또는 >
문자가 포함되어 있지 않다는 것을 알고 있는 경우 다음을 수행할 수 있습니다.
sed '
# replace opening quote with <
s|\[quote=[^]]*\]|<|g
# and closing quotes with >
s|\[/quote\]|>|g
:1
# work our way from the inner quotes
s|<[^<>]*>||g
t1'
<
또는 문자가 포함될 수 있는 경우 >
다음과 같은 구성표를 사용하여 이스케이프할 수 있습니다.
sed '
# escape < and > (and the escaping character _ itself)
s/_/_u/g; s/</_l/g; s/>/_r/g
<code-above>
# undo escaping after the work has been done
s/_r/>/g; s/_l/</g; s/_u/_/g'
AND perl
, 재귀 정규식 사용:
perl -pe 's@(\[quote=[^\]]*\](?:(?1)|.)*?\[/quote\])@@g'
아니면 당신이 언급한 것처럼:
perl -pe 's@(\[quote=.*?\](?:(?1)|.)*?\[/quote\])@@g'
를 사용하면 perl
옵션을 추가하여 여러 줄 입력을 처리할 수 있습니다 -0777
. 의 경우 sed
코드 앞에 다음 접두사를 추가해야 합니다.
:0
$!{
N;b0
}
그러면 전체 입력이 패턴 공간에 로드됩니다.
답변2
나는 이것을 확인했고 그것은 나를 위해 일했습니다. 대신 다른 임시 모드를 선택할 수도 있습니다 foobar
. 이 모드가 없으면 sed
태그 사이의 모든 항목이 제거되고 하나만 남습니다.text part 1 text part 3
sed -e 's/\/quote\]/foobar\]/3' -e 's/\[.*\/quote\]//' -e 's/\[.*foobar]//' testfile
대신 testfile
에 파이프할 수 있다면cat
답변3
모든 시작 인용문에서 카운터 변수를 증가시키고 모든 닫는 인용문에서 카운터 변수를 감소시키는 작은 스크립트입니다. 카운터 변수가 더 크면 0
텍스트 조각을 건너뜁니다.
#!/bin/bash
# disable pathname expansion
set -f
cnt=0
for i in $(<$1); do
# start quote
if [ "${i##[quote=}" != "$i" ] && [ "${i: -1}" = "]" ]; then
((++cnt))
elif [ "$i" = "[/quote]" ]; then
((--cnt))
elif [ $cnt -eq 0 ]; then
echo -n "$i "
fi
done
echo
산출:
$ cat q1
text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3
$ ./parse.sh q1
text part 1 text part 2 text part 3
$ cat q2
text part 1 [quote=foo] outer quote 1 [quote=bar] inner quote [foo] [/quote] outer quote 2 [/quote] text part 2 [quote=foo-bar] next quote [/quote] text part 3
$ ./parse.sh q2
text part 1 text part 2 text part 3
답변4
POSIX sed
여기의 자세한 지침에 따라 이 작업을 수행 할 수 있습니다 . 이 솔루션은 표시된 두 입력 모두에 적용됩니다. 필요한 변환을 달성하기 위해 줄 바꿈을 마커로 사용하기 때문에 입력 제한은 여러 줄이 아닙니다.
$ sed -e '
:top
/\[\/quote]/!b
s//\
&/
s/\[quote=/\
\
&/
:loop
s/\(\n\n\)\(\[quote=.*\)\(\[quote=.*\n\)/\2\1\3/
tloop
s/\n\n.*\n\[\/quote]//
btop
' input.txt