【问题标题】:Newlines at the end "$( .... )" get removed in shell scripts. Why?末尾的换行符 "$( .... )" 在 shell 脚本中被删除。为什么?
【发布时间】:2013-06-04 04:24:45
【问题描述】:

谁能解释一下为什么这两个命令的输出不同?

$ echo "NewLine1\nNewLine2\n"
NewLine1
NewLine2
                         <-- Note 2nd newline here
$ echo "$(echo "NewLine1\nNewLine2\n")"
NewLine1
NewLine2
$                        <-- No second newline

有什么好的方法可以将新行保留在 "$( .... )" 中的输出末尾吗?我考虑过只添加一个虚拟字母并将其删除,但我很想了解为什么这些新行会消失。

【问题讨论】:

  • 它的回声本质是消耗空白。我现在没有时间进行测试,但是您对问题测试的 dbl-quotes 可能没有按照您的想法进行。也许echo "$(echo \"NewLine1\nNewLine2\n\")" 会起作用。祝你好运。
  • 谢谢,但您不需要在 "$(....)" 语句的括号内转义双引号。它被视为一个新命令。这是使用 $(...) 而不是 ticks ``的优点之一。上述问题也可以通过使用刻度来重现。
  • 记录在案的行为:(gnu.org/software/bash/manual/bashref.html#Command-Substitution) -- “...命令的标准输出,所有尾随换行符都被删除”。为了解决这个问题,您可能必须将输出重定向到临时文件,然后使用该文件。
  • 这是一个很好的解释。这有点令人沮丧,因为我有一个脚本,其中 "$( ... )" 语句末尾的新行对我很重要。就像我上面说的,我可能会附加一个随机字母,然后将其剥离。
  • @glennjackman:您应该将其发布为答案。

标签: shell unix scripting ksh


【解决方案1】:

命令替换删除每个尾随换行符。

删除一个是有意义的。例如:

basename foo/bar

输出bar\n。在:

var=$(basename foo/bar)

您希望$var 包含bar,而不是bar\n

然而在

var=$(basename $'foo/bar\n')

您希望$var 包含bar\n(毕竟,换行符与Unix 文件名中的任何字符一样有效)。但所有 shell 都会删除 每个 尾随换行符。这个错误出现在原始的 Bourne shell 中,甚至修复了 Bourne 大部分缺陷的rc 也没有修复那个。 (尽管 rc 具有 ``(){cmd} 语法,不会删除任何换行符)。

在 POSIX shell 中,要解决此问题,您可以:

var=$(basename -- "$file"; echo .)
var=${var%??}

虽然你会失去basename 的退出状态。您可以通过以下方式修复:

var=$(basename -- "$file" && echo .) && var=${var%??}

${var%??} 是去掉最后两个字符。第一个是我们上面添加的.,第二个是basename 添加的 one 换行符,我们不再删除,因为命令替换会像其他换行符一样,如果有的话,将是我们想要获取基础的文件名的一部分,所以我们确实想要它们。

在没有 ${var%x} 运算符的 Bourne shell 中,您必须采取漫长而复杂的方式来解决它。

【讨论】:

    【解决方案2】:

    因为that's what POSIX specifies 在 Bourne shell 中一直都是这样:

    2.6.3 命令替换

    命令替换允许替换命令的输出 代替命令名称本身。应发生命令替换 当命令包含如下:

    $(命令)

    或(反引号版本):

    `命令`

    shell 应该通过执行命令来扩展命令替换 在子外壳环境中(请参阅外壳执行环境)和 替换命令替换(命令文本加上 将 "$()" 或反引号) 与 命令,删除一个或多个 字符的序列 替换结束。结束前嵌入 字符 不得删除输出;但是,它们可能被视为 字段分隔符并在字段拆分期间消除,具体取决于 IFS 的值和有效的引用。如果输出包含 任何空字节,行为未指定。

    保留最终换行符的一种方法是

    VAR="$(command; echo x)"   # Append x to keep newline(s).
    VAR=${VAR%x}               # Chop x.
    

    可见:

    $ x="$(whoami; echo x)" ; printf '<%s>\n' "$x" "${x%x}"
    <user
    x>
    <user
    >
    

    但是为什么要删除尾随的换行符呢?因为很多时候你想要这样。我也在用 perl 编程,我无法计算读取一行或变量然后需要删除换行符的次数:

    while (defined ($string = <>)) {
        chop $string;
        frobnitz($string);
    } 
    

    【讨论】:

    • VAR="$(command)x" 不保留最后的换行符,因为它是命令替换剥离它们。
    • @sch 糟糕,已修复。感谢您的提醒。
    【解决方案3】:

    如果没有删除换行符,则构造如下:

    x="$(pwd)/filename"
    

    不会有用,但编写 Unix 的人更喜欢有用的行为。

    有一次,简而言之,很久以前(比如 1983 年,也许是 1984 年),我遇到了一个特定 Unix 变体的 shell 更新,它没有删除尾随的换行符。它破坏了所有地方的脚本。很快就修好了。

    【讨论】:

      猜你喜欢
      • 2018-04-08
      • 1970-01-01
      • 2013-04-28
      • 1970-01-01
      • 1970-01-01
      • 2012-01-27
      • 1970-01-01
      • 1970-01-01
      • 2015-08-19
      相关资源
      最近更新 更多