416

STR="Hello\nWorld"
echo $STR

作为输出产生

Hello\nWorld

代替

Hello
World

我应该怎么做才能在字符串中有换行符?

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

4

13 回答 13

441

如果您使用的是 Bash,则解决方案是使用$'string',例如:

$ STR=$'Hello\nWorld'
$ echo "$STR" # quotes are required here!
Hello
World

如果您使用几乎任何其他 shell,只需在字符串中插入换行符:

$ STR='Hello
> World'

巴什很不错。\n它接受的不仅仅是$''字符串。以下是 Bash 手册页的摘录:

Words of the form $'string' are treated specially. The word expands to
string, with backslash-escaped characters replaced as specified by the
ANSI C standard. Backslash escape sequences, if present, are decoded
as follows:
      \a     alert (bell)
      \b     backspace
      \e
      \E     an escape character
      \f     form feed
      \n     new line
      \r     carriage return
      \t     horizontal tab
      \v     vertical tab
      \\     backslash
      \'     single quote
      \"     double quote
      \nnn   the eight-bit character whose value is the octal value
             nnn (one to three digits)
      \xHH   the eight-bit character whose value is the hexadecimal
             value HH (one or two hex digits)
      \cx    a control-x character

The expanded result is single-quoted, as if the dollar sign had not
been present.

A double-quoted string preceded by a dollar sign ($"string") will cause
the string to be translated according to the current locale. If the
current locale is C or POSIX, the dollar sign is ignored. If the
string is translated and replaced, the replacement is double-quoted.
于 2010-07-05T22:43:03.403 回答
194

Echo 已经九十年代了,充满了危险,以至于它的使用应该会导致不低于 4GB 的核心转储。说真的,echo 的问题是 Unix 标准化进程最终发明该printf实用程序并消除所有问题的原因。

因此,要在字符串中获取换行符,有两种方法:

# 1) Literal newline in an assignment.
FOO="hello
world"
# 2) Command substitution.
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

那里!没有 SYSV 与 BSD 的回声疯狂,一切都得到了整齐的打印和对 C 转义序列的完全可移植的支持。请大家printf立即使用,满足您的所有输出需求,永不回头。

于 2012-05-16T12:06:03.393 回答
67

我根据其他答案所做的是

NEWLINE=$'\n'
my_var="__between eggs and bacon__"
echo "spam${NEWLINE}eggs${my_var}bacon${NEWLINE}knight"

# which outputs:
spam
eggs__between eggs and bacon__bacon
knight
于 2012-12-01T11:26:47.543 回答
37

我发现-e国旗优雅而直接

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

如果字符串是另一个命令的输出,我只使用引号

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"
于 2015-06-05T07:14:54.233 回答
31

问题不在于外壳。问题实际上出在echo命令本身,以及变量插值周围缺少双引号。您可以尝试使用echo -e,但并非所有平台都支持,printf现在建议使用其中一个原因是为了便于携带。

您也可以尝试将换行符直接插入到您的 shell 脚本中(如果您正在编写脚本),这样它看起来像......

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

或等效地

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
于 2010-06-09T13:19:29.277 回答
22
  1. 唯一简单的替代方法是在变量中实际键入一个新行:

    $ STR='new
    line'
    $ printf '%s' "$STR"
    new
    line
    

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

  2. 一个字符有几个等价物new line

    \n           ### A common way to represent a new line character.
    \012         ### Octal value of a new line character.
    \x0A         ### Hexadecimal value of a new line character.
    

    但是所有这些都需要某些工具( 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.
    

    因此,需要该工具来构建带有换行符的字符串:

    $ STR="$(printf 'new\nline')"
    $ printf '%s' "$STR"
    new
    line
    
  3. 在某些 shell 中,序列$'是一种特殊的 shell 扩展。已知在 ksh93、bash 和 zsh 中工作:

    $ STR=$'new\nline'
    
  4. 当然,更复杂的解决方案也是可能的:

    $ echo '6e65770a6c696e650a' | xxd -p -r
    new
    line
    

    或者

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line
    
于 2016-03-29T23:44:43.903 回答
8

单引号 '...\n...' 前的 $ 如下所示,但双引号不起作用。

$ echo $'Hello\nWorld'
Hello
World
$ echo $"Hello\nWorld"
Hello\nWorld
于 2017-04-11T20:17:35.783 回答
4

我不是 bash 专家,但这个对我有用:

STR1="Hello"
STR2="World"
NEWSTR=$(cat << EOF
$STR1

$STR2
EOF
)
echo "$NEWSTR"

我发现这更容易格式化文本。

于 2016-08-14T10:57:43.700 回答
3

免责声明:我首先写了这个,然后偶然发现了这个问题。我认为此解决方案尚未发布,并看到 tlwhitec 确实发布了类似的答案。我仍然发布这个,因为我希望它是一个有用和彻底的解释。

简短的回答:

这似乎是一个非常便携的解决方案,因为它适用于相当多的外壳(见评论)。
这样,您可以将真正的换行符放入变量中。

此解决方案的好处是您不必在源代码中使用换行符,因此您可以以任何您想要的方式缩进代码,并且该解决方案仍然有效。这使它变得健壮。它也是便携式的。

# 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='
'

...但这不太健壮,并且很容易因意外缩进代码或事后忘记将其缩进而损坏,这使得在(缩进)函数中使用不方便,而早期的解决方案是健壮的。

现在,至于双引号:在命令替换周围加
双引号的原因是,您甚至可以在变量赋值前加上关键字或内置函数(例如在函数中),它仍然适用于所有 shell ,否则shell会遇到麻烦,因为破折号否则会丢失'q'并且你最终会得到一个空的'nl'变量(同样,由于命令替换)。 用另一个例子可以更好地说明这个问题:"nl="$(printf '\nq')"localdash

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.
于 2020-11-21T00:25:45.980 回答
0

在我的系统(Ubuntu 17.10)上,无论是从命令行(into sh)键入还是作为sh脚本执行时,您的示例都可以正常工作:

[bash]§ sh
$ STR="Hello\nWorld"
$ echo $STR
Hello
World
$ exit
[bash]§ echo "STR=\"Hello\nWorld\"
> echo \$STR" > test-str.sh
[bash]§ cat test-str.sh 
STR="Hello\nWorld"
echo $STR
[bash]§ sh test-str.sh 
Hello
World

我想这回答了你的问题:它只是工作。(我没有试图弄清楚细节,例如换行符替换的确切时间\n发生在什么时候sh)。

但是,我注意到这个相同的脚本在执行时会表现不同bash并且会打印出来Hello\nWorld

[bash]§ bash test-str.sh
Hello\nWorld

我设法通过以下方式获得所需的输出bash

[bash]§ STR="Hello
> World"
[bash]§ echo "$STR"

注意 . 周围的双引号$STR。如果保存并作为bash脚本运行,则其行为相同。

以下还给出了所需的输出:

[bash]§ echo "Hello
> World"
于 2017-10-22T11:37:20.370 回答
0

那些只需要换行符并鄙视破坏缩进的多行代码的挑剔者可以这样做:

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

Bash(以及可能的其他 shell)在命令替换后会吞噬所有尾随换行符,因此您需要以printf非换行符结束字符串,然后将其删除。这也可以很容易地成为一个单行者。

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

我知道这是两个动作而不是一个动作,但是我的缩进和可移植性强迫症现在很平静 :) 我最初开发它是为了能够拆分仅换行符分隔的输出,最后我使用了一个\r用作终止字符的修改。这使得换行符即使对于以 . 结尾的 dos 输出也有效\r\n

IFS="$(printf '\n\r')"
于 2018-08-28T11:18:57.390 回答
0

我对这里的任何选项都不满意。这对我有用。

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")
于 2019-03-25T10:36:54.470 回答
0

这并不理想,但我编写了很多代码并以与问题中使用的方法类似的方式定义了字符串。公认的解决方案要求我重构大量代码,因此我将所有代码替换\n"$'\n'",这对我有用。

于 2020-07-31T15:09:57.373 回答