对您的问题的快速回答是将您的输出通过管道传输到另一个 cmd,例如 awk:
sed 'commands' file | awk '(NR>1){printf "%s\n",l}{l=$0}END{printf "%s",l}'
这将删除最后一个<newline>。 sed 无法做到这一点,下面的答案试图解释它。更多可能性可以在How can I delete a newline if it is the last character in a file?找到
为什么sed 总是以<newline> 结尾?
这个问题的答案取决于对标准的解释以及你使用的sed的实现。
根据sed posix standard:
在默认操作中,sed 会循环追加一行输入,
减去它的终止 <newline> 字符,进入模式空间。
如果 <newline> 在模式中,则应跳过从输入中读取
D 命令结束前一个循环之前的空间。 sed 实用程序
然后应按顺序应用其地址选择的所有命令
模式空间,直到命令开始下一个循环或退出。如果不
命令明确地开始一个新的循环,然后在脚本结束时
模式空间应复制到标准输出(-n 除外
已指定)并且应删除模式空间。 每当
模式空间写入标准输出或命名文件,sed 应
立即使用 <newline> 跟随它。
这意味着两件事:
- 如果一行没有被
<newline> 终止,则不会处理该行。
- 写入标准输出的任何内容都以
<newline> 终止,即作为命令周期结束或发出命令p 或P 的结果输出。
示例:sed (SunOS 5.10) SUNWcsu 11.10.0 rev=2005.01.21.15.53
$ echo -n foo | sed 'p'
$ echo -n 'foo\nbar' | sed 'p'
foo
foo
如果没有以<newline> 终止的行,显然没有处理。否则,<newlines> 将添加到任何输出。
MacOS sed manual 与 posix 有类似的解释。
通常,sed 循环复制一行输入,不包括其
终止换行符,变成
一个模式空间,(除非在 D 函数之后还有一些东西),应用所有的命令
使用选择该模式空间的地址,将模式空间复制到标准输出,追加追加
ing 一个换行符,并删除模式空间。
因为我没有mac,所以没有测试。
GNU sed manual 对此事的看法似乎略有不同:
sed 通过在每一行输入上执行以下循环来运行:
首先,sed 从输入流中读取一行,删除任何尾随
换行符,并将其放置在模式空间中。然后命令是
执行;每个命令都可以有一个与之关联的地址:地址
是一种条件码,一个命令只有在
在执行命令之前验证条件。
当到达脚本末尾时,除非-n 选项正在使用,否则模式空间的内容将打印到输出流中,如果尾随换行符被删除,则将其添加回来。
这意味着以下内容:
- 所有行都被处理,全部或不被
<newline>终止
- 如果到达命令周期的末尾,则会添加与最初删除相同数量的
<newline>。
例如:sed (GNU sed) 4.2.2
在以下示例中,仅在p 之后添加换行符,而不是在循环结束之后。 (换行符是十六进制的012)
$ echo -n foo | hexdump -b
0000000 146 157 157
0000003
$ echo -n foo | sed --posix 'p' | hexdump -b
0000000 146 157 157 012 146 157 157
0000007
这是Footnote 7解释的:
实际上,如果sed 打印出没有终止换行符的行,它
尽管如此,只要有更多文本,就会打印丢失的换行符
发送到相同的输出流,这给出了“最不期望
惊喜”,即使它不会发出像 sed -n p 这样的命令
与cat 完全相同。
结论:根据posix standard,您最终会得到一个以<newline> 结尾的输出文件,但它可能不是输入的最后一行。根据Gnu manual,您的输出以与输入文件末尾相同的数量终止。
问题: GNU 的 sed --posix 是真正的 posix 吗?