这个 sed 命令在做什么: sed '1!G;h'

这个 sed 命令在做什么: sed '1!G;h'

我试图更好地理解 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(打印)命令。

另一种说法是“对于每一行输入,它以相反的方式输出所有先前的行(最后是新行)”。

所以代码会

  1. alpha入模式空间,复制alpha到保留空间,打印alpha
  2. beta入模式空间,\nalpha从保留空间追加到模式空间,复制beta\nalpha到保留空间,打印beta\nalpha
  3. gamma入模式空间,\nbeta\nalpha从保留空间追加到模式空间,复制gamma\nbeta\nalpha到保留空间,打印gamma\nbeta\nalpha

代码

sed '1!G' <<END_INPUT
alpha
beta
gamma
END_INPUT

输出

alpha
beta

gamma

(第三行和最后一行为空)。

sed代码输出每一行输入,并将向除第一行之外的所有行添加额外的换行符。它这样做是因为它会

  1. alpha入模式空间,打印alpha
  2. beta入模式空间,\n从保留空间追加到模式空间,打印beta\n
  3. 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. 1sed 接收seq (a ) (第一行)中的第一个数字。该线被放置在模式空间中。
  2. 要执行的第一个命令是G,它附加一个换行符,后跟保留空间到现在的格局空间。首先(如此处)保留空间是空的,因此 sed 将换行符和(保留空间的)空字符串附加到当前模式空间。简而言之,它将换行符附加到输入的第一行。
  3. 第二个命令是h。将模式空间复制到保留空间。现在,保留空间包含一个1后跟一个换行符。
  4. 命令列表已结束,因此 sed 打印模式空间(a1后跟换行符),因为 sed 没有-n选项。然后循环到下一个输入行。
  5. 现在 sed 接收第二行 a 2,将其放置在模式空间中。
  6. 第一个命令是 G。因此,sed 将保留空间(a1和换行符,如上所述,以较短的形式编写1\n:)附加到模式空间。现在模式空间成立2\n1\n
  7. 图案空间所容纳的内容被打印出来。 sed 循环返回。
  8. 在第三行,G附加\n2\n1\n3.
  9. 使用将该模式空间3\n2\n1\n复制到保留空间h
  10. 此外,模式空间会打印在 sed 脚本的末尾。
  11. 循环并重复直到最后一行。

以上所有内容都相当于以相反的顺序捕获线条。

这个脚本可能会让事情变得更清楚(用 引用模式空间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

仅打印最后一次迭代,所有行均按相反顺序排列。

相关内容