我试图更好地理解 sed,但尽管反复浏览在线文档和手册页,有一件事让我感到困惑。
如果我这样做:(在 内bash
)
echo "
alpha
beta
gamma" | sed '1!G;h'
我懂了:
alpha
beta
alpha
gamma
beta
alpha
所以我知道1!G
除了第 1 行之外,每个输入行后面都会添加一个新行。然后h
将模式空间复制到保留空间中。但是保持空间没有任何反应,并且应该输出模式空间,此时我希望看到1!G;h
与 完全相同的输出1!G
,即:
alpha
beta
gamma
保持空间是否以某种方式输出?如果保留空间只是在每行的模式空间之后输出,那么为什么不将sed 'h'
每行加倍呢?
哦,我在 macOS Mojave 5 上运行这个程序bash
。我不知道正在sed
使用哪个版本,但它是最新 macOS 附带的基础版本。
答案1
您忘记了G
从保留空间追加,这是您的第一个命令写入的,但不是第二个命令写入的。
sed h
并不是将每一行加倍,因为它只是覆盖每个周期中的保留空间。
你的第一个命令相当于
sed '1!G;h' <<END_INPUT
alpha
beta
gamma
END_INPUT
和输出
alpha
beta
alpha
gamma
beta
alpha
对于除第一行之外的所有行,它将使用定界换行符将当前保留空间附加到模式空间。然后它用模式空间覆盖保持空间。在每个周期结束时(处理一行输入后),有一个隐式p
(打印)命令。
另一种说法是“对于每一行输入,它以相反的方式输出所有先前的行(最后是新行)”。
所以代码会
- 读
alpha
入模式空间,复制alpha
到保留空间,打印alpha
, - 读
beta
入模式空间,\nalpha
从保留空间追加到模式空间,复制beta\nalpha
到保留空间,打印beta\nalpha
, - 读
gamma
入模式空间,\nbeta\nalpha
从保留空间追加到模式空间,复制gamma\nbeta\nalpha
到保留空间,打印gamma\nbeta\nalpha
。
代码
sed '1!G' <<END_INPUT
alpha
beta
gamma
END_INPUT
输出
alpha
beta
gamma
(第三行和最后一行为空)。
此sed
代码输出每一行输入,并将向除第一行之外的所有行添加额外的换行符。它这样做是因为它会
- 读
alpha
入模式空间,打印alpha
, - 读
beta
入模式空间,\n
从保留空间追加到模式空间,打印beta\n
, - 读
gamma
入模式空间,\n
从保留空间追加到模式空间,打印gamma\n
。
请注意,从此处的保留空间追加G
只会在模式空间的末尾添加一个额外的换行符,因为它始终为空(因为您从不写入它)。
答案2
我通过尝试发现:
]# echo -e "a\nb\nc\nd\ne\nf" |sed '1!G;h'
a
b
a
c
b
a
d
c
b
a
e
d
c
b
a
f
e
d
c
b
a
如果你倒着读:“abcdef”,然后是“abcde”,然后是“abcd”,“abc”,“ab”,“a”。
你管那叫什么?
它有效,因为“G”添加,“h”替换。如果你离开,G;h
你会在这些块之间得到额外的空行。
info sed
有一个“tac”示例:
sed -n '1!G;$p;h'
通过只打印一次(-n
和$p
),结果是完全不同的:它是最后一个“块”sed '1!G;h'
Perl 可以使用(命名的)变量轻松模仿此算法:
]# echo -e "a\nb\nc" | perl -ne '$hold = $_ . $hold; print $hold;'
a
b
a
c
b
a
反转来自于线和保存的线的合并方式:
]# echo -e "a\nb\nc" |perl -ne '$hold .= $_; print $hold;'
a
a
b
a
b
c
$_
是输入线。它是 H=H+line 与 H=line+H。你必须info sed
仔细阅读才能发现这一点以及其他微妙之处。
答案3
让我们从更简单的事情开始:
$ seq 4 | sed 'G;h'
1
2
1
3
2
1
4
3
2
1
每个块添加一个新数字并以相反的顺序打印它。
为什么会发生这种情况?我们慢慢走一下这个过程:
1
sed 接收seq (a ) (第一行)中的第一个数字。该线被放置在模式空间中。- 要执行的第一个命令是
G
,它附加一个换行符,后跟保留空间到现在的格局空间。首先(如此处)保留空间是空的,因此 sed 将换行符和(保留空间的)空字符串附加到当前模式空间。简而言之,它将换行符附加到输入的第一行。 - 第二个命令是
h
。将模式空间复制到保留空间。现在,保留空间包含一个1
后跟一个换行符。 - 命令列表已结束,因此 sed 打印模式空间(a
1
后跟换行符),因为 sed 没有-n
选项。然后循环到下一个输入行。 - 现在 sed 接收第二行 a
2
,将其放置在模式空间中。 - 第一个命令是 G。因此,sed 将保留空间(a
1
和换行符,如上所述,以较短的形式编写1\n
:)附加到模式空间。现在模式空间成立2\n1\n
。 - 图案空间所容纳的内容被打印出来。 sed 循环返回。
- 在第三行,
G
附加\n2\n1\n
到3
. - 使用将该模式空间
3\n2\n1\n
复制到保留空间h
。 - 此外,模式空间会打印在 sed 脚本的末尾。
- 循环并重复直到最后一行。
以上所有内容都相当于以相反的顺序捕获线条。
这个脚本可能会让事情变得更清楚(用 引用模式空间l
):
$ seq 5 | sed -n 'G;h;l'
1\n$
2\n1\n$
3\n2\n1\n$
4\n3\n2\n1\n$
5\n4\n3\n2\n1\n$
而且,作为您的原始脚本,我们避免在第一行添加换行符:
$ seq 5 | sed -n '1!G;h;l'
1$
2\n1$
3\n2\n1$
4\n3\n2\n1$
5\n4\n3\n2\n1$
现在,使用 alpha、beta 和 gamma:
$ printf 'alpha\nbeta\ngamma' | sed -n '1!G;h;l'
alpha$
beta\nalpha$
gamma\nbeta\nalpha$
我们回到原来的 sed 脚本:
$ printf 'alpha\nbeta\ngamma' | sed '1!G;h'
alpha
beta
alpha
gamma
beta
alpha
实际上,国际海事组织应该是:
$ printf 'alpha\nbeta\ngamma' | sed '1!G;h;$!d'
gamma
beta
alpha
仅打印最后一次迭代,所有行均按相反顺序排列。