避免这种影响

避免这种影响

为什么结构$(...)删除结尾换行符

#!/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'

相关内容