以下は、私が解決したい問題の単純化されたバージョンです。私はこのファイルを持っています:
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:
残りの文字をすべて最後まで一致させました。状態と長さの間に1点しかないことがわかっている場合は、\.
代わりに使用してください。
sed -r 's/^[0-9]+[ \t]+H[ \t]+([0-9]+[ \t]+[A-Za-z]+)\.(Content-Length:)?([ \t]+[0-9]+)?.*/\1\3/' file
1行の文も問題ありませんが、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はデフォルトでファイルから一度に1行だけ読み取られるため、\n
データには表示できません。したがって、仮分離膜として安全に使用することができる。