为什么我不能在 bash 的引号中生成换行符?

Why can't I generate newlines in quotes in bash?

无论我做什么,我都无法让引号中的换行符保留在输出中。我是什么做的?这是 OS X 中的 bash。我的代码在这里:Gist

read -d '' sbt_text=$(cat <<"EOF"
#!/bin/bash
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M";
java $SBT_OPTS -jar `dirname [=11=]`/sbt-launch.jar "$@"
EOF
)

echo $sbt_text

#!/bin/bash > SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M"; > java -jar /sbt-launch.jar ""

你应该引用你的变量:

echo "$sbt_text"

这可以防止 Bash 进行 单词拆分 (和 通配符 ,即扩展通配符,例如 *?) 在替换你的变量(参数扩展)之后。请注意,为 read 设置 IFS 不会阻止 echo 命令行上的分词。试试这个好玩的:

( IFS= ; echo $sbt_text )

对比

IFS= echo $sbt_text ### won't work

第二个示例将不起作用 - subshell (( )) 在这里是必需的,因为 IFS 被调用 shell 解释为相反的分词在问题的代码示例中被 read 解释。

当然这也有效(但是 "ruins" 你的 IFS):

IFS= ; echo $sbt_text

你可能想要的是:

IFS= read -d '' -r sbt_text <<"EOF"
#!/bin/bash
SBT_OPTS="-Xms512M -Xmx1536M -Xss1M -XX:+CMSClassUnloadingEnabled -XX:MaxPermSize=256M";
java $SBT_OPTS -jar `dirname [=10=]`/sbt-launch.jar "$@"
EOF

echo "$sbt_text"

虽然您的 read 命令无法像发布的那样工作,但 不使用双引号 $sbt_text 将始终执行 word splitting (and also pathname expansion (globbing)),当传递给 echo 时,意味着任何内部白色 space 都标准化为 单个 space .

为了在 POSIX-like shell 中保持变量 原样 的值 - 不对其进行处理对于上面描述的 shell expansions - 你必须 双引号 它。

关于read命令,注意:

  • -r 可防止对输入中的 \ 个实例进行通常无意的解释。
  • 前置 IFS= 确保此处文档中的每一行相对于白色 space 保持原样(不修剪前导和尾随白色 space)
  • 不需要 command substitution ($(...)) with cat, because << (a here-document) 直接提供 stdin 输入,read 从中获取输入。
    • 通过在您的 read 命令中使用 sbt_text=$(...),您实际上将命令替换 ($(...)) 的输出作为(最终无效的)变量的一部分name 而不是提供 input,导致 read 仍然等待 stdin 输入。
    • 使用just赋值sbt_text=$(cat <<"EOF" ...)(没有read)几乎可以给你你想要的东西(尽管以调用外部实用程序为代价cat), 除了命令替换会删除任何尾随的换行符。