\n 在 heredoc 中的变量中

\n in variable in heredoc

有没有办法让 Bash heredoc 解释 heredoc 中的 '\n\'?

我在循环中有一个迭代构建的字符串,类似于

for i in word1 word2 word3
do
        TMP_VAR=$i
        ret="$ret\n$TMP_VAR"
done

然后我想在 heredoc 中使用创建的字符串:

cat <<EOF > myfile
HEADER
==
$ret
==
TRAILER
EOF

但是我想将“\n”字符解释为换行符,因此输出是

HEADER
==
word1
word2
word3
==
TRAILER

而不是

HEADER
==
\nword1\nword2\nword3
==
TRAILER

可能吗?或者我是否应该以其他方式构建我的初始字符串?

在bash中,您可以使用$'\n'向字符串添加换行符:

ret="$ret"$'\n'"$TMP_VAR"

您还可以使用 += 附加到字符串:

ret+=$'\n'"$TMP_VAR"

正如其他人(以及对其他问题的其他答案)所说,您可以将编码字符放入字符串中供 shell 解释。

x=$'\n' # newline
printf -v x '\n' # newline

也就是说,我认为没有任何方法可以直接将编码的换行符放入 heredoc

cat <<EOF
\n
EOF

只是输出文字 \n

cat <<$'EOF'
…
EOF

没有什么特别的,<<'EOF'

也没有

你能做的最好的事情就是对换行符进行预编码,并在 heredoc 中包含扩展:

nl=$'\n'
cat <<EOF
foo bar $nl baz
EOF

产出

foo bar
 baz

最好的解决方案是用实际的换行符构建变量,而不是插入需要用换行符替换的字符序列。

我发现以下函数非常有用,所以我将其放入我的 bash 启动文件中;对于你这个简单的案例,它会完美地工作:

lines() { printf %s\n "$@"; }

有了它,你可以写:

ret=$(lines word1 word2 word3)

而不是你使用的循环。然后你可以将 $ret 插入到 heredoc 中,它将按预期工作。 [见注释 1]

但是,如果出于某种原因你真的想用转义序列而不是实际字符来构造你的字符串,你可以使用 bash printf 内置的扩展功能进行扩展, %b 格式代码。 %b 执行几乎 相同的转义转换,但有一些不同之处。有关详细信息,请参阅 help printf。使用它您可以执行以下操作:

$ ret="word1\nword2\nword3"
$ cat <<EOF > tmp
> HEADER
> ==
> $(printf "%b" "$ret")
> ==
> TRAILER
> EOF
$ cat tmp
HEADER
==
word1
word2
word3
==
TRAILER

备注

  1. lines函数的使用有一个微妙之处。 printf 不断重复其格式字符串,直到吸收所有参数,因此格式 %s\n 每个 参数(包括最后一个)之后放置一个换行符。对于大多数用例,这正是您想要的;我对行的大部分使用都与将结果输入需要输入行的实用程序有关。

    但在 ret=$(lines word1 word2 word3) 的情况下,我真的不想要结尾的换行符,因为我的计划是在此处的文档中单独插入一行 $ret。幸运的是,命令替换 ($(...)) 总是从命令的输出中删除尾随的换行符,因此赋值后 ret 的值在 参数之间有换行符,但不是在最后。 (这个功能有时会很烦人,但更多时候它正是你想要的,所以它不会被注意到。)

变化:

==
$ret
==

至:

==
$(echo -e $ret)
==

使用 awk '{print}' 解决了我的问题。

cat << EOF |
line1
line2
line3
EOF
awk '{print}' > outputfile
# outputfile contents
cat outputfile
line1
line2
line3