쉘 스크립트에서 웹 서버의 응답을 구문 분석하려고 합니다. 응답은 다음과 같습니다.
HTTP/1.0 404 NOT FOUND
Content-Length: 223
Content-Type: application/json
Last-Modified: Fri, 21 Aug 2020 15:24:23 GMT
Cache-Control: public, max-age=43200
Expires: Sat, 22 Aug 2020 08:04:19 GMT
ETag: "1598023463.02863-223-4034336499"
Date: Fri, 21 Aug 2020 20:04:19 GMT
Server: Werkzeug/1.0.1 Python/3.8.5
{
"message": {
"status": "404",
"message": "Not Found"
}
}
변수에 할당합니다.
% foo="$(curl -i http://127.0.0.1/404)"
하나의 변수가 아닌 상태 코드용 변수 하나와 응답 본문용 변수 하나를 원합니다. 상태 코드를 얻는 것은 쉽습니다.
% echo "$foo" | head -n 1
어려운 부분은 sed를 사용하여 헤더를 필터링하는 것입니다. 기반으로브루스 바넷(Bruce Barnett) 멋진 Sed 그리모아르, 내 생각엔 이것이 효과가 있을 것 같아요:
% echo "$foo" | sed '1,/^$/ d'
또는:
% echo "$foo" | sed -n '/^$/,$ p'
그러나 두 명령의 결과는 아무것도 아닙니다. 이유를 모르겠습니다.
중요한 경우 Homebrew의 zsh 5.8 및 GNU sed 4.8을 사용하고 Mac OS의 컬 7.64.1을 사용하고 있습니다.
답변1
RFC7230에서는 헤더가 CR-LF 쌍, 그 뒤에 CRLF 쌍(CRLF - CRLF)(느슨한 용어: 빈 줄), HTTP 응답 "본문"으로 구분되어야 합니다. 따라서 일반 http/1.1에는 일부가 포함됩니다.입력하다.
\n\n
Unix에서 설명하는 것처럼 헤더에 끝이 없는 "빈 줄"이 없습니다 . 이는 또한 sed의 경우 a가 ^$
헤더 끝에 있는 빈(DOS) 라인과 일치하지 않음을 의미합니다. 해당 라인에 \r
(캐리지 리턴)이 포함되어 있기 때문입니다. (GNU) sed에서 이 (거의) 빈 줄을 감지하는 다른 방법은 다음과 같습니다 ^\r$
.
$ printf '%s\n' "$foo" | sed '1,/^\r$/ d'
캐리지 리턴 제거
캐리지 리턴 제거가 적용되는 경우 http 응답(서버가 내보낼 전체 http/1.1 메시지)에는 \n\n
헤더와 본문을 구분하기 위해 두 개의 연속 줄 바꿈( )으로 빈 줄이 포함됩니다.
그렇다면 특수 값인 null RS
(awk의 단락 모드)이 이 헤더를 처리할 수 있습니다.
$ echo "$foo" | tr -d '\r' | awk -v RS="" 'NR>1'
또는 이메일 본문의 빈 줄이 유지되도록 하려면 다음을 수행하세요.
$ echo "$foo" | tr -d '\r' | awk 'BEGIN{ORS=RS="\n\n"}NR>1'
캐리지 리턴 허용
그러나 메시지(RFC5322와 같은) 및 http 응답(RFC7230과 같은 전체 http/1.1 메시지)은 다음 CR NL
과 같이 사용해야 합니다.제목의 줄 끝 태그. RS는 다음을 포함할 수 있습니다.임의로 선택할 수 있는캐리지 리턴에는 정규식이 필요하며 상수가 아니기 때문에 RT(레코드 종결자)를 사용합니다. 이는 GNU awk를 사용해야 함을 의미합니다.
$ echo "foo" | awk 'BEGIN{RS="(\r?\n){2}"}NR>1{printf "%s%s",$0,RT}'
{
"message": {
"status": "404",
"message": "Not Found"
}
}
답변2
문제는 컬의 출력에 캐리지 리턴(CR)이 있으므로 /^$/
각 줄에 CR이 있어 비어 있지 않기 때문에 패턴이 일치하지 않는다는 것입니다.
CR을 삭제하거나 설명하는 등 몇 가지 작업을 수행할 수 있습니다.
foo="$(curl -i http://127.0.0.1/404 | tr -d '\r')"
삭제한 다음
printf '%s\n' "$foo" | sed '1,/^$/d'
작동할 것입니다. 또는 다음을 사용하여 CR을 삭제하지 않은 경우tr
printf '%s\n' "$foo" | sed $'1,/^\r$/d'
zsh는 문자열 교체를 수행할 수 있으므로 다음을 사용하는 경향이 있습니다.
printf '%s\n' "${foo#*$'\r\n\r\n'}"
또는
printf '%s\n' "${foo#*$'\n\n'}"
tr
sed 프로세스를 저장하기 위해 CR을 제거 했는지 여부에 따라 다릅니다 .
하지만 경고가 있습니다: 명령 대체 막대모두후행 개행 문자(캐리지 리턴 아님) HTTP 응답은 입니다 <header1>CRLF...<headern>CRLFCRLF<body>
. <body>
비어 있으면 CR이 $foo
포함되거나 제거된 경우에만 <header1>CRLF...<headern>CRLFCR
해당 됩니다. <header1>CRLF...<headern>
이러한 경우 *$'\r\n\r\n'
OR이 *$'\n\n'
일치하지 않으며 헤더가 제거되지 않습니다.
어쨌든 임의의 문자열과 개행 문자를 인쇄하려면 구문은 다음과 같습니다.
printf '%s\n' "$foo" # POSIX
print -r - "$foo" # ksh/zsh
echo -E - "$foo" # zsh
백슬래시(json에서는 일반적임) 또는 다음으로 시작하는 일부 값(json에서는 해당되지 않음)이 포함되어 있으면 Not이 제대로 작동하지 않습니다 .echo "$foo"
$foo
-