解释这个sed条件分支行为
我有以下(gnu)解释这个sed条件分支行为,sed,Sed,我有以下(gnu)sed脚本,用于解析另一个sed脚本,并在单独的一行上输出不同的命令 换句话说,这个脚本应该在每个分号后面加一个换行符,但在匹配或替换命令中的分号除外 Sed脚本: #!/bin/sed -rf # IDEA: # replace ';' by ';\n' except when it's inside a match expression or subst. expression. # Ignored patterns: /^#/b # commented lines
sed
脚本,用于解析另一个sed脚本,并在单独的一行上输出不同的命令
换句话说,这个脚本应该在每个分号后面加一个换行符代码>,但在匹配或替换命令中的分号除外
Sed脚本:
#!/bin/sed -rf
# IDEA:
# replace ';' by ';\n' except when it's inside a match expression or subst. expression.
# Ignored patterns:
/^#/b # commented lines
/^$/b # empty lines
# anything in a single line, without semicolon except at the end
/^[^\n;]*;?$/b
# Processed patterns (put on separate lines):
# Any match preceding a semicolon, or the end of the line, or a substitution
s_/^[^/]+/[^;s]*;?_&\n_; t printtopline
s/^\\(.)[^\1]+\1[^;s]*;?/&\n/;t printtopline
# Any substitution (TODO)
# Any other command, separated by semicolon
s/\;/\;\n/; t printtopline;
:printtopline
P;D; # print top line, delete it, start new cycle
例如,我使用以下文件对其进行了测试(实际上改编自@ctac_uuu的一段文字,改为我以前的sed问题):
输入文件:
#!/bin/sed -f
#/^>/N;
:A;
/\n>/!{s/\n/ /;N;bA}; # join next line if not a sequence label
#h;
#s/\(.*\)\n.*/\1/p;
s/^>//g;P
#x;
#s/.*\n//;
D
bA;
s/\;/\;\n/; t printtopline;
:printtopline
$ sed 's/;/;\n/' <<< 'one;two;three;four'
one;
two;three;four
$ sed 's/;/;\n/; P;D;' <<< 'one;two;three;four'
one;
two;
three;
four
输出
上面的脚本生成正确的输出,例如,行/\n>/!{s/\n/;n;bA};#如果不是序列标签,则连接下一行
变为:
/\n>/!{s/\n/ /;
N;
bA};
# join next line if not a sequence label
问题:
但是,您能否帮助我理解为什么脚本的这一部分起作用:
#!/bin/sed -f
#/^>/N;
:A;
/\n>/!{s/\n/ /;N;bA}; # join next line if not a sequence label
#h;
#s/\(.*\)\n.*/\1/p;
s/^>//g;P
#x;
#s/.*\n//;
D
bA;
s/\;/\;\n/; t printtopline;
:printtopline
$ sed 's/;/;\n/' <<< 'one;two;three;four'
one;
two;three;four
$ sed 's/;/;\n/; P;D;' <<< 'one;two;three;four'
one;
two;
three;
four
?
在我看来,分支命令tprinttopline
在这里是无用的。我想无论替换成功与否,接下来要执行的是:printtopline
但是,如果我注释掉t
命令,或者将其替换为b
,脚本将生成以下输出行:
/\n>/!{s/\n/ /;
N;bA}; # join next line if not a sequence label
从info sed
,这里是对t
的解释:
为什么t
命令后面紧跟着它的标签的行为根本不像no命令或b
命令?第1部分-p;D序列工作。
比较这两个命令的输出:sed的/;/\n/”
和sed/;/\n/;PD、 "代码>
第一名:
#!/bin/sed -f
#/^>/N;
:A;
/\n>/!{s/\n/ /;N;bA}; # join next line if not a sequence label
#h;
#s/\(.*\)\n.*/\1/p;
s/^>//g;P
#x;
#s/.*\n//;
D
bA;
s/\;/\;\n/; t printtopline;
:printtopline
$ sed 's/;/;\n/' <<< 'one;two;three;four'
one;
two;three;four
$ sed 's/;/;\n/; P;D;' <<< 'one;two;three;four'
one;
two;
three;
four
如果replaced
标志为true,则执行t
命令。它正在将此标志更改为false
:
case 't':
if (replaced)
{
replaced = false;
因此,在第一个,s/\;/\\n/;t印刷背线代码>情况下,替换成功-因此,replaced
标志设置为true。然后,以下t
命令正在运行,并将replaced
标志更改回false
在second情况下,不使用t
command-s/\;/\\n/代码>,替换也成功-因此,replaced
标志设置为true
但是现在,该标志被存储到下一个循环,由D
命令启动。因此,第一个t
命令出现在新循环中-s_/^[^/]+/[^;s]*_&\n_;t printtopline
,它检查替换的
标志,查看标志是否为真
,并跳转到标签:printtopline
,忽略标签前的所有其他命令
模式空间没有换行符,因此p;D代码>序列只打印模式空间,并用新的输入行开始下一个周期。关键部分是:
仅当在读取最后一个输入行或执行条件分支后成功进行了替换时,才执行分支到标签
也就是说,t
回顾过去,并考虑到直到最近一次的所有替换的成功
- 输入,或
- 条件分支
考虑一下你正在询问的输入行。在我们所有的替换之后
/\n>/!{s/\n/ /;
N;bA}; # join next line if not a sequence label
当我们到达p;D代码>。P
命令输出第一行,然后D
删除第一行并重新启动主循环。现在我们只有
N;bA}; # join next line if not a sequence label
请注意,这并不涉及阅读任何其他行。没有输入发生D
刚刚删除了部分图案空间
我们将处理剩余的文本(因为其他模式都不匹配,所以不会执行任何操作),直到到达代码的这一部分:
s_/^[^/]+/[^;s]*;?_&\n_; t printtopline
替换失败(模式空间不包含/^
)。但是t
命令不检查仅此s
命令的状态;它查看自最近的输入或条件分支执行以来所有替换的历史
最近的输入发生在/\n>/!{s/\n/;n;bA}代码>已读取
最近执行的条件分支为
s/\;/\;\n/; t printtopline;
:printtopline
在代码的原始版本中。此后,没有其他替代成功,因此t
命令不起任何作用。计划的其余部分按预期继续进行
但在修改后的代码版本中,此时没有条件分支(b
是无条件分支):
这意味着来自s_/^[^/]+/[^;s]*;的t
_&\n_;t printtopline
“查看”了s/\;/\\n/
成功了,所以它立即跳到P;D代码>零件。这是什么输出
N;bA}; # join next line if not a sequence label
未经修改
总之:t
在这里起作用,不是因为它直接跳到标签上,而是因为它作为下一个执行的t
的动态分隔符。在这里没有t
,下一个t
将考虑之前执行的s
命令,因此,分支到标签在这里并不重要。实际上,我的示例显示了这一点,这就是我想要理解的(省略我认为我得到的p;D
部分)。@PlasmaBinturong Yes,我只是被我自己的例子迷住了,忘记了你的:)。@PlasmaBinturong我已经更新了答案。请参阅第2部分。我不认为某个特定sed实现的源代码提供了有用的解释。它没有说明为什么用这种方式使用replaced
,也没有说明将来的版本是否会有所不同。用sed语言解释(参考文档)会更好。@melpomene我认为,两者在连接中都很有用。源代码有助于理解它为何以这种方式工作。主要思想是-存在一些标志(变量、字段、数组的元素-实现细节无关紧要)