This

STR="Hello\nWorld"
echo $STR

作为输出产生

Hello\nWorld

而不是

Hello
World

我应该做什么有一个换行在字符串?

注意:此问题与echo无关。 我知道echo -e,但我正在寻找一种解决方案,允许传递一个字符串(其中包括换行符)作为参数到其他命令,没有类似的选项来解释\n的换行符。


当前回答

免责声明:我首先写了这篇文章,然后偶然发现了这个问题。我以为这个解决方案还没有发布,看到tlwhitec确实发布了一个类似的答案。我仍然把这个贴出来,因为我希望这是一个有用和彻底的解释。

简短的回答:

这似乎是一个相当可移植的解决方案,因为它可以在相当多的shell上工作(参见评论)。 通过这种方式,可以在变量中获得真正的换行符。

这种解决方案的好处是您不必在源代码中使用换行符,因此可以缩进 按照您想要的方式编写代码,解决方案仍然有效。这使得它健壮。它也很便携。

# Robust way to put a real newline in a variable (bash, dash, ksh, zsh; indentation-resistant).
nl="$(printf '\nq')"
nl=${nl%q}

再答:

以上方案说明:

换行符通常会因为命令替换而丢失,但是为了防止这种情况,我们添加了一个'q',然后删除它。(使用双引号的原因将在下文进一步解释。)

我们可以证明变量包含一个实际的换行符(0x0A):

printf '%s' "$nl" | hexdump -C
00000000  0a  |.|
00000001

(请注意,'%s'是需要的,否则printf将把文字'\n'字符串转换为实际的0x0A字符,这意味着我们什么也证明不了。)

当然,除了这个答案中提出的解决方案,人们也可以使用这个(但是…):

nl='
'

... 但是这种方法的健壮性较差,并且很容易因为不小心缩进代码,或者后来忘记缩进代码而被破坏,这使得在(缩进的)函数中使用不方便,而早期的解决方案是健壮的。

Now, as for the double quotes: The reason for the double quotes " surrounding the command substitution as in nl="$(printf '\nq')" is that you can then even prefix the variable assignment with the local keyword or builtin (such as in functions), and it will still work on all shells, whereas otherwise the dash shell would have trouble, in the sense that dash would otherwise lose the 'q' and you'd end up with an empty 'nl' variable (again, due to command substitution). That issue is better illustrated with another example:

dash_trouble_example() {
    e=$(echo hello world) # Not using 'local'.
    echo "$e" # Fine. Outputs 'hello world' in all shells.

    local e=$(echo hello world) # But now, when using 'local' without double quotes ...:
    echo "$e" # ... oops, outputs just 'hello' in dash,
              # ... but 'hello world' in bash and zsh.

    local f="$(echo hello world)" # Finally, using 'local' and surrounding with double quotes.
    echo "$f" # Solved. Outputs 'hello world' in dash, zsh, and bash.

    # So back to our newline example, if we want to use 'local', we need
    # double quotes to surround the command substitution:
    # (If we didn't use double quotes here, then in dash the 'nl' variable
    # would be empty.)
    local nl="$(printf '\nq')"
    nl=${nl%q}
}

上述解决方案的实际示例:

# Parsing lines in a for loop by setting IFS to a real newline character:

nl="$(printf '\nq')"
nl=${nl%q}

IFS=$nl

for i in $(printf '%b' 'this is line 1\nthis is line 2'); do
    echo "i=$i"
done

# Desired output:
# i=this is line 1
# i=this is line 2

# Exercise:
# Try running this example without the IFS=$nl assignment, and predict the outcome.

其他回答

在单引号'…\n…,但是双引号不能用。

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld

唯一简单的替代方法是在变量中输入新行: STR = '新的美元 线的 $STR" %s " 新 行

是的,这意味着在代码中需要的地方输入Enter。

有几个新行字符的等价物。 一种常用的表示新行字符的方法。 新行字符的八进制值。 新行字符的十六进制值。

但所有这些都需要一些工具(POSIX printf)的“解释”:

    echo -e "new\nline"           ### on POSIX echo, `-e` is not required.
    printf 'new\nline'            ### Understood by POSIX printf.
    printf 'new\012line'          ### Valid in POSIX printf.
    printf 'new\x0Aline'       
    printf '%b' 'new\0012line'    ### Valid in POSIX printf.

因此,这个工具需要用new-line来构建一个字符串:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line

在某些shell中,序列$'是一个特殊的shell展开。 已知在ksh93, bash和zsh中工作: STR =美元“新\在线” 当然,也可能有更复杂的解决方案: $ echo '6e65770a6c696e650a' | XXD -p -r 新 行

Or

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line

这并不理想,但我已经编写了大量代码,并以类似于问题中使用的方法定义了字符串。被接受的解决方案要求我重构大量代码,因此,我将每个\n替换为“$'\n'”,这对我来说很有效。

那些只需要换行符而不喜欢破坏缩进的多行代码的挑剔的人可以这样做:

IFS="$(printf '\nx')"
IFS="${IFS%x}"

Bash(可能还有其他shell)在命令替换之后会占用所有尾随的换行符,因此需要用非换行符结束printf字符串,然后删除它。这也可以很容易地成为一个联机程序。

IFS="$(printf '\nx')" IFS="${IFS%x}"

我知道这是两个动作而不是一个,但我的缩进和可移植性强迫症现在是和平的:)我最初开发这个是为了能够分割换行分离的输出,我最终使用了一个修改,使用\r作为结束字符。这使得换行分割甚至适用于以\r\n结尾的dos输出。 IFS = " $ (printf ' r \ n \ ')”

问题不在于外壳。问题实际上出在echo命令本身,以及变量插值时缺少双引号。您可以尝试使用echo -e,但并非所有平台都支持它,这也是现在推荐使用printf的原因之一,因为它具有可移植性。

您还可以尝试直接在shell脚本中插入换行符(如果您正在编写脚本),因此它看起来像…

#!/bin/sh
echo "Hello
World"
#EOF

或者同样的

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!