有人可以解释为什么这两个命令的输出不同吗?
$ echo "NewLine1\nNewLine2\n"
NewLine1
NewLine2
<-- Note 2nd newline here
$ echo "$(echo "NewLine1\nNewLine2\n")"
NewLine1
NewLine2
$ <-- No second newline
有没有什么好方法可以让新行保留在输出结尾的&#34; $(....)&#34; ?我已经考虑过添加一个虚拟字母并将其删除,但我很想知道为什么这些新线条会消失。
答案 0 :(得分:3)
因为that's what POSIX specifies并且在Bourne shell中一直如此:
2.6.3命令替换
命令替换允许替换命令的输出 代替命令名称本身。命令替换将发生 当命令括起来如下:
$(命令)
或(反引号):
`command`
shell应通过执行命令扩展命令替换 在子shell环境中(请参阅Shell执行环境)和 替换命令替换(命令文本加上 将“$()”或反引号括起来与标准输出 命令,删除一个或多个&lt; newline&gt;的序列人物 替换结束。嵌入式&lt; newline&gt;结束前的字符 输出不得删除;但是,他们可能被视为 字段分隔符并在字段拆分期间消除,具体取决于 IFS的值和有效的引用。如果输出包含 任何空字节,行为都是未指定的。
保留最终换行符的一种方法是
VAR="$(command; echo x)" # Append x to keep newline(s).
VAR=${VAR%x} # Chop x.
显示:
$ x="$(whoami; echo x)" ; printf '<%s>\n' "$x" "${x%x}"
<user
x>
<user
>
但为什么要删除尾随换行符?因为通常你会这样想。我也在使用perl进行编程,我无法计算读取行或变量的次数,然后需要删除换行符:
while (defined ($string = <>)) {
chop $string;
frobnitz($string);
}
答案 1 :(得分:2)
如果未删除换行符,则构造如:
x="$(pwd)/filename"
不会有用,但编写Unix的人更喜欢有用的行为。
很久以前(就像1983年,也许是1984年),我曾经在Unix的特定变种上进行了shell更新,但没有删除尾随的换行符。它打破了整个地方的脚本。很快就解决了。
答案 2 :(得分:1)
命令替换会删除每个尾随换行符。
删除一个是有意义的。例如:
basename foo/bar
输出bar\n
。在:
var=$(basename foo/bar)
您希望$var
包含bar
,而不是bar\n
。
然而在
var=$(basename $'foo/bar\n')
您希望$var
包含bar\n
(毕竟,换行符与Unix上的文件名中的任何字符一样有效)。但是所有shell都会移除每个尾随换行符。这种错误是在最初的Bourne shell中,甚至rc
已经修复了Bourne的大部分缺陷并没有解决这个问题。 (尽管rc
具有``(){cmd}
语法,不会删除任何换行符。)
在POSIX shell中,要解决此问题,您可以执行以下操作:
var=$(basename -- "$file"; echo .)
var=${var%??}
虽然您正在失去basename
的退出状态。你可以解决的问题:
var=$(basename -- "$file" && echo .) && var=${var%??}
${var%??}
是删除最后两个字符。第一个是我们在上面添加的.
,第二个是由basename
添加的一个换行符,我们不再删除命令替换会执行的操作其他换行符(如果有的话)将成为我们想要获得的文件名的一部分,所以我们确实需要它们。
在没有${var%x}
运算符的Bourne shell中,你必须采用漫长而复杂的方式来解决它。