以下是我正在努力解决的问题的简化版本。我有这个文件:
1 H 200 OK.Content-Length: 2422 x
2 H 403 Forbidden.z
我尝试让 sed 匹配 HTTP 状态和 - 如果存在 - 内容长度,如下所示:
> 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
NB 我也尝试了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]+)
matches 1 H (200 OK)
,然后.*
匹配到末尾,并且(Content-Length: ([0-9]+))?
next.*
都匹配行末尾的空字符串。
在非贪婪版本中,第一个.*?
尝试尽可能少地匹配,但这没什么,因为该行的其余部分 ( .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
因此无法从数据中显示。因此,它可以安全地用作临时分隔符。