为什么壳结构$(...)
删除结尾换行符?
#!/bin/bash
S='a
b
'
printf '%i [%s]\n' "${#S}" "$S"
T=$(printf '%s' "$S")
printf '%i [%s]\n' "${#T}" "$T"
打印出这个:
4 [a
b
]
3 [a
b]
该变量S
包含 2 个换行符。第一个printf
打印正确。然后第二个printf
打印并通过评估S
分配完全相同的结果。最后第三个表明T
$(...)
printf
中缺少最后一个换行符T
!为什么?
我使用 sh、ksh 和 Zsh 得到了相同的结果。
答案1
这是有记录的行为;man bash
:
Bash 通过在子 shell 环境中执行命令并将命令替换替换为命令的标准输出来执行扩展,并删除所有尾随换行符。
当然,你可能会说这并没有回答“为什么?”问题。
我不知道官方的解释。我的猜测是这是有道理的,因为这是你通常需要的。添加换行符也比删除换行符更容易。
# This is how people are used to print text
echo foo
# In the other scenario this would end in two newlines at the end
echo foo "$(whatever)"
# could behandled with echo -n or printf but who would consider that an improvement?
另一个原因是某些命令在末尾添加换行符,而其他命令则不添加。此行为减轻了用户关心这种差异的负担。
避免这种影响
您可以这样做以获得真正的命令输出:
T=$(whatever; echo x)
T_real="${T%x}"
答案2
为什么?无用,因为标准是这么说的:
...用命令的标准输出替换命令替换,删除替换末尾的一个或多个 <newline> 字符序列。
实际上,可能是因为它总是这样工作(据我所知,POSIX 主要是对现有行为进行编码)。
更有用的是,您可以这样做:
echo "$(date +"%F %T") $(hostname): something happened..."
代替
t=$(date +"%F %T")
t=${t%
}
h=$(hostname)
h=${h%
}
echo "$t $h: something happened..."
也就是说,大多数打印某些单个值的命令可能会在末尾打印换行符,以便它们在命令行上使用更友好。 (shell 不必检测光标不在命令后的行首;zsh 会这样做,而大多数其他人则不会。)但是,如果您要在脚本中使用该值来执行其他操作,您可能会这样做不需要换行符,只需要内容。这不仅适用于打印,还适用于将值作为参数传递给某些命令。
常见的解决方法是添加额外字符并删除它:
foo=$(printf 'foo\n'; echo .)
foo=${foo%.}
echo "<$foo>"
或者,如果您只有printf
内部命令替换,请printf -v
在可用的情况下使用:
printf -v foo 'foo\n'