为什么 n 而不是 b 或 d 或什么都不改变 sed 在此脚本中的行为?
Why does an n instead of b or d or nothing change the behaviour of sed in this script?
在回答问题 时,我在 sed
中遇到了一个我无法解释的行为 — 你能解释吗?
数据文件:data
Goodbye
select *
from dep
where jkdsfj
select *
from sal
where jkdsfj
select elephants
from abject poverty
join flying tigers
where abelone = shellfish;
select mouse
from toolset
join animals where tail = cord
and buttons = legs
Hello
objective 是 select 单词 from
和 where
之间的文本。
这里有脚本的 4 个变体:
script.16
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; }
script.17
# Bust by final n;
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; n; }
script.18
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; d; }
script.19
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; b
}
这些都适用于 BSD (Mac OS X) sed
和 GNU sed
。最后一个脚本可以使用 b; }
,它可以与 GNU sed
一起工作,但 BSD sed
拒绝它。
问题是 script.17
的输出与其他 3 个不同,我不明白为什么:
$ sed -n -f script.16 data
dep
sal
abject poverty
join flying tigers
toolset
join animals
$ sed -n -f script.17 data
dep
select *
abject poverty
toolset
and buttons = legs
Hello
$
为什么输出的是 select *
和 and buttons = legs
和 Hello
?
$ sed -n -f script.18 data
dep
sal
abject poverty
join flying tigers
toolset
join animals
$ sed -n -f script.19 data
dep
sal
abject poverty
join flying tigers
toolset
join animals
$
为什么 n
的使用会像这样改变 sed
的行为?从我尝试使用诊断 'printing' 的一些变体来看,似乎 n
阻止 sed
在正确看到 where
时进行识别,但是 b
和 d
都跳转到下一个循环,就像 n
通常那样,但有些不同。
鉴于两个独立的实现做同样的事情,我不得不假设这是故意的,但是......为什么?
总结
问题在于范围以及评估范围时模式 space 中的内容。
sed 中的范围端点在评估范围时与模式 space 的内容相匹配,而不是相对于原始输入行。因此,对于 sed -n '/start/,/end/{...}'
,重要的是命令开头的模式 space 中的内容,而不是命令处理后的模式 space 中的内容或 [=17] =] 已导致读取更多行。
简单示例
p;n
与范围结合的问题可以用更简单的代码来说明。请注意,与 b
和 d
不同,命令 n
读取一行。因此,sed -n 'p;n'
每隔一行打印一次。例如:
$ seq 5 | sed -n 'p;n'
1
3
5
现在,结合范围观察 p;n
:
$ seq 5 | sed -n '/1/,/3/{p;n;}'
1
3
以上按预期工作。然而,以下内容令人惊讶:
$ seq 5 | sed -n '/1/,/2/{p;n;}'
1
3
5
包含2
的行被n
命令读入,然后立即被丢弃。当评估范围 /1/,/2/
时,包含 2
的行不会出现在模式 space 中。因此,sed
永远不会看到 /1/,/2/
的结尾,它会继续认为它在范围内。
脚本 17
现在,让我们考虑您的脚本 17,稍作修改:
sed -n '/from/,/where/ { s/.*from */BEGIN/; s/ *where.*/END/; /^ *$/d; p; n; }' data
BEGINdep
select *
END
BEGINabject poverty
END
BEGINtoolset
and buttons = legs
Hello
在这里,我们看到 /from/,/where/
的范围从出现 from
到下一次 where
出现在模式缓冲区 开始处评估范围时的命令 。 n
读取的 where
实例永远不会结束范围。
进一步演示
考虑范围 /1/,/END/
,其中 END
从未出现在文件中:
$ seq 5 | sed -n 's/3/END/; /1/,/END/{p;n}'
1
END
尽管 END
从未出现在文件中,但在评估范围时它出现在模式 space 中。因此,它结束了范围。
作为再一次演示,让我们改变一下上述命令的顺序。在下面,我们看到 END
并没有结束范围,尽管它被打印出来了:
$ seq 5 | sed -n ' /1/,/END/{s/3/END/; p; n}'
1
END
5
这是因为计算范围时 END
不在模式 space 中。因此,sed
永远不会看到范围的尽头。
在回答问题 sed
中遇到了一个我无法解释的行为 — 你能解释吗?
数据文件:data
Goodbye
select *
from dep
where jkdsfj
select *
from sal
where jkdsfj
select elephants
from abject poverty
join flying tigers
where abelone = shellfish;
select mouse
from toolset
join animals where tail = cord
and buttons = legs
Hello
objective 是 select 单词 from
和 where
之间的文本。
这里有脚本的 4 个变体:
script.16
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; }
script.17
# Bust by final n; /from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; n; }
script.18
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; d; }
script.19
/from/,/where/ { s/.*from *//; s/ *where.*//; /^ *$/d; p; b }
这些都适用于 BSD (Mac OS X) sed
和 GNU sed
。最后一个脚本可以使用 b; }
,它可以与 GNU sed
一起工作,但 BSD sed
拒绝它。
问题是 script.17
的输出与其他 3 个不同,我不明白为什么:
$ sed -n -f script.16 data
dep
sal
abject poverty
join flying tigers
toolset
join animals
$ sed -n -f script.17 data
dep
select *
abject poverty
toolset
and buttons = legs
Hello
$
为什么输出的是 select *
和 and buttons = legs
和 Hello
?
$ sed -n -f script.18 data
dep
sal
abject poverty
join flying tigers
toolset
join animals
$ sed -n -f script.19 data
dep
sal
abject poverty
join flying tigers
toolset
join animals
$
为什么 n
的使用会像这样改变 sed
的行为?从我尝试使用诊断 'printing' 的一些变体来看,似乎 n
阻止 sed
在正确看到 where
时进行识别,但是 b
和 d
都跳转到下一个循环,就像 n
通常那样,但有些不同。
鉴于两个独立的实现做同样的事情,我不得不假设这是故意的,但是......为什么?
总结
问题在于范围以及评估范围时模式 space 中的内容。
sed 中的范围端点在评估范围时与模式 space 的内容相匹配,而不是相对于原始输入行。因此,对于 sed -n '/start/,/end/{...}'
,重要的是命令开头的模式 space 中的内容,而不是命令处理后的模式 space 中的内容或 [=17] =] 已导致读取更多行。
简单示例
p;n
与范围结合的问题可以用更简单的代码来说明。请注意,与 b
和 d
不同,命令 n
读取一行。因此,sed -n 'p;n'
每隔一行打印一次。例如:
$ seq 5 | sed -n 'p;n'
1
3
5
现在,结合范围观察 p;n
:
$ seq 5 | sed -n '/1/,/3/{p;n;}'
1
3
以上按预期工作。然而,以下内容令人惊讶:
$ seq 5 | sed -n '/1/,/2/{p;n;}'
1
3
5
包含2
的行被n
命令读入,然后立即被丢弃。当评估范围 /1/,/2/
时,包含 2
的行不会出现在模式 space 中。因此,sed
永远不会看到 /1/,/2/
的结尾,它会继续认为它在范围内。
脚本 17
现在,让我们考虑您的脚本 17,稍作修改:
sed -n '/from/,/where/ { s/.*from */BEGIN/; s/ *where.*/END/; /^ *$/d; p; n; }' data
BEGINdep
select *
END
BEGINabject poverty
END
BEGINtoolset
and buttons = legs
Hello
在这里,我们看到 /from/,/where/
的范围从出现 from
到下一次 where
出现在模式缓冲区 开始处评估范围时的命令 。 n
读取的 where
实例永远不会结束范围。
进一步演示
考虑范围 /1/,/END/
,其中 END
从未出现在文件中:
$ seq 5 | sed -n 's/3/END/; /1/,/END/{p;n}'
1
END
尽管 END
从未出现在文件中,但在评估范围时它出现在模式 space 中。因此,它结束了范围。
作为再一次演示,让我们改变一下上述命令的顺序。在下面,我们看到 END
并没有结束范围,尽管它被打印出来了:
$ seq 5 | sed -n ' /1/,/END/{s/3/END/; p; n}'
1
END
5
这是因为计算范围时 END
不在模式 space 中。因此,sed
永远不会看到范围的尽头。