打印包含换行符的变量时,为什么最后一个换行符被删除?

Har*_*her 9 shell command-substitution newlines

file.txt 的内容(不奇怪,POSIX 定义的文本文件)

iguana
gecko
anole
Run Code Online (Sandbox Code Playgroud)

示例脚本:

#!/bin/sh

string="$(cat file.txt)"

printf '%s' "$string"
Run Code Online (Sandbox Code Playgroud)

示例输出:

[coolguy@somemachine ~]$ ./script.sh
iguana
gecko
anole[coolguy@somemachine ~]$
Run Code Online (Sandbox Code Playgroud)

最后一个换行符发生了什么?为什么除了最后一个都保留了换行符?如果已经有一个换行符,我们似乎不必使用 echo 添加一个换行符。

ilk*_*chu 25

这不是打印,而是执行此操作的命令替换。它被定义为这样做。从POSIX 描述

shell 应通过在子 shell 环境中执行命令并用命令的标准输出替换命令替换来扩展命令替换,在替换结束时删除一个或多个 {newline} 字符的序列

请注意,它会删除所有尾随换行符,而不仅仅是一个。

在比较常见的情况下,您会使用命令替换来捕获单行输出,例如osrev=$(uname -r). 这些实用程序通常会打印一个尾随换行符,以方便用户在命令行上使用。但是在 shell 脚本中,您可能希望将该字符串用作另一个字符串的一部分,例如文件名:filename=blahblah-$osrev.dat. 在这种情况下,尾随的换行符只会令人讨厌。

当然,echo在任何情况下,plain都会添加最后的换行符。


如果您希望文件的内容在变量中保持原样,那么常见的解决方法是在命令替换中添加一个额外的字符,然后将其删除:

printf "foo\nbar\n\n" > file
string=$(cat file; echo x)
string=${string%x}
printf '%q\n' "$string"
Run Code Online (Sandbox Code Playgroud)

输出$'foo\nbar\n\n',显示出现的两个尾随换行符。


根据您打算对数据做什么,可能还有其他方法。例如,while read循环或 Bash 的mapfile,如果您碰巧想逐行处理文件。