sedを使用して最初の空の行と最後の行の間の行を表示するには?

sedを使用して最初の空の行と最後の行の間の行を表示するには?

シェルスクリプトでWebサーバーの応答を解析しようとしています。応答は次のとおりです。

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)"

1つの変数ではなく、ステータスコード用の1つの変数と応答本文用の1つの変数が必要です。ステータスコードを取得するのは簡単です。

% 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のcurl 7.64.1を使用しています。

答え1

RFC7230では、ヘッダーはCR-LFペア、その後にCRLFペア(CRLF - CRLF)(緩い用語:空行)、HTTPレスポンスの「本文」で区切る必要があります。したがって、通常のhttp/1.1には一部が含まれます。入力する

\n\nUnixで説明したように、ヘッダーに無限の空白行がありません。これはまた、sedの場合、aがヘッダーの^$末尾にある空(DOS)行と一致しないことを意味します。対応する行に\r(キャリッジリターン)が含まれているためです。 (GNU)sedでこの(ほぼ)空の行を検出する別の方法は次のとおりです^\r$

$ printf '%s\n' "$foo" | sed '1,/^\r$/ d'

キャリッジリターンの削除

キャリッジリターンの削除が適用される場合、http応答(サーバーがエクスポートする完全なhttp / 1.1メッセージ)には、\n\nヘッダーと本文を区別するために2つの連続する改行()で空白行が含まれます。

そうであれば、特別な値である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'}"

trsedプロセスを保存するために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-

関連情報