다음은 제가 해결하려는 문제의 단순화된 버전입니다. 나는 이 파일을 가지고 있습니다 :
1 H 200 OK.Content-Length: 2422 x
2 H 403 Forbidden.z
나는 HTTP 상태와 - 존재하는 경우 - 컨텐츠 길이와 일치하도록 sed를 얻으려고 시도했습니다.
> sed -nEe 's,^.*H ([ 0-9a-zA-Z]+).*(Content-Length: ([0-9]+))?.*$,\1 \3,p' x
200 OK
403 Forbidden
따라서 선택적 그룹은 절대 일치하지 않습니다. 물음표를 제거하여 선택 사항이 아닌 것으로 만들면 내용 길이와 일치하지만 물음표가 없는 줄은 일치하지 않습니다.
> sed -nEe 's,^.*H ([ 0-9a-zA-Z]+).*(Content-Length: ([0-9]+)).*$,\1 \3,p' x
200 OK 2422
다음 출력을 제공하도록 sed를 얻으려면 어떻게 해야 합니까?
200 OK 2422
403 Forbidden
주의: 지연 일치( )를 지원하는 Perl도 시도했지만 .*?
성공하지 못했습니다.
> perl -pe 's,^.*H ([ 0-9a-zA-Z]+).*?(Content-Length: ([0-9]+))?.*?$,\1 \3,' x
200 OK
403 Forbidden
답변1
그룹은 선택 사항 이므로 (Content....)
욕심 많은 버전에서는 다음에 대해 다음을 수행합니다.
1 H 200 OK.Content-Length: 2422 x
^.*H ([ 0-9a-zA-Z]+)
match 1 H (200 OK)
, 그런 다음 .*
끝까지 일치하고 (Content-Length: ([0-9]+))?
다음 .*
둘 다 줄 끝의 빈 문자열과 일치합니다.
non-greedy 버전에서는 첫 번째는 .*?
가능한 한 적게 일치하려고 시도하지만 나머지 줄( .Content-Length: 2422 x
)은 일치하고 (Content-Length: ([0-9]+))?
그 이후 .*?$
줄 끝까지는 모두 일치하지 않기 때문에 괜찮습니다.
.*
첫 번째 캡처 이후 첫 번째 항목이 a 를 스크롤하지 않는지 확인해야 합니다 Content-Length: \d+
. 예를 들어 각 단계에서 부정적인 예측을 사용합니다.
perl -lne 'print if
s/^.*?H ([\s\w]+)(?:(?!Content-Length: \d+).)*(?:Content-Length: (\d+))?.*$/\1\2/'
간단히 이렇게 할 수도 있지만:
perl -lne '
if (/H\s+([\s\w]+)(.*)/) {
my $status = $1;
print "$status" . ($2 =~ /Content-Length: (\d+)/ && " $1");
}'
또는:
sed -nE 's/^.*H[[:space:]]+([[:space:][:alnum:]]+).*Content-Length: ([[:digit:]]+).*$/\1 \2/p;t
s/^.*H[[:space:]]+([[:space:][:alnum:]]+).*$/\1/p'
즉, 한 번에 대체하려고 시도하지 마십시오.
답변2
sed에서 정규 표현식은 탐욕적입니다. .*
이전에 Content-Length:
나머지 문자를 모두 끝까지 일치시켰 습니다 . 상태와 길이 사이에 단 하나의 지점만 있다는 것을 알고 있다면 \.
대신 사용하십시오.
sed -r 's/^[0-9]+[ \t]+H[ \t]+([0-9]+[ \t]+[A-Za-z]+)\.(Content-Length:)?([ \t]+[0-9]+)?.*/\1\3/' file
한 줄짜리 명령문도 괜찮지만 sed에는 //
if와 유사한 명령문도 있습니다. 아래 스크립트를 사용하여 실행하세요 sed -rf script file
.
s/^[0-9]+[ \t]+H[ \t]+([0-9]+[ \t]+[A-Za-z]+)/\1\n/
/\n.*Content-Length:([ \t]+[0-9]+).*/ s//\1\n/
s:\n.*::
sed는 기본적으로 파일에서 한 번에 한 줄만 읽으므로 \n
데이터에서 표시할 수 없습니다. 따라서 임시 분리막으로 안전하게 사용할 수 있습니다.