如何在sh的字符串中包含换行符?


336

这个

STR="Hello\nWorld"
echo $STR

作为输出产生

Hello\nWorld

代替

Hello
World

如何在字符串中包含换行符?

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

Answers:


352

解决方案是使用$'string',例如:

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

这是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.

74
这是有效的bash,但不是POSIX sh。
jmanning2k 2012年

7
+ 1只是一张纸条:出口varDynamic = “$ varAnother1 $ varAnother2” $ '\ n'
约尔丹Georgiev的

3
实际上,确实可以按照OP的要求使用文字字符串,但是一旦要替换变量,它就会失败:STR=$"Hello\nWorld"只需prints即可Hello\nWorld
ssc 2016年

3
@ssc单引号,而不是双引号
miken32 '17

7
@ssc在您的示例中没有变量。同样,此方法需要单引号。最后,要包含插值变量,必须将双引号和特殊单引号的字符串连接在一起。例如,H="Hello"; W="World"; STR="${H}"$'\n'"${W}"; echo "${STR}"将在单独的行上回显“ Hello”和“ World”
JDS

166

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

因此,要在字符串中获取换行符:

FOO="hello
world"
BAR=$(printf "hello\nworld\n") # Alternative; note: final newline is deleted
printf '<%s>\n' "$FOO"
printf '<%s>\n' "$BAR"

那里!没有SYSV vs BSD的回音疯狂,所有内容都被整齐地打印,并且完全支持C换码序列。大家请printf现在就使用,永远不要回头。


3
感谢您的回答,我认为它提供了非常好的建议。但是请注意,我最初的问题不是关于如何获取回显以打印换行符,而是如何使换行符进入shell变量。您还展示了如何使用printf来做到这一点,但是我认为使用amphetamachine的解决方案$'string'更加干净。
Juan A. Navarro

11
仅用于“干净”的定义不足。$'foo'自2013年起,该语法是无效的POSIX语法。许多shell都会抱怨。
詹斯

5
BAR=$(printf "hello\nworld\n") 不为我打印尾随\ n
乔尼

3
@Jonny不应该;命令替换的shell规范说,输出末尾的一个或多个换行符被删除。我意识到这对于我的示例来说是意外的,并对其进行了编辑以在printf中包含换行符。
詹斯(Jens)

5
@ismael否,$()由POSIX指定为在替换结束时删除一个或多个<newline>字符的序列
詹斯2014年

61

我根据其他答案所做的是

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

29

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

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

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

或同等

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

23

我发现-e国旗优雅而直截了当

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

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

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"

2
其他答案中已经提到了这一点。此外,虽然它已经在那里了,我只是编辑要强调的事实,这个问题的问题不是有关echo
Juan A. Navarro

这不是问题的答案
Rohit Gupta 2015年

这完全可以回答我正在寻找的问题!谢谢!
Kieveli 2015年

1
在这里为我工作得很好。我曾经根据此回声创建文件,并且在输出中正确生成了新行。
马特乌斯·莱昂

echo -e因其可移植性问题而臭名昭著。与POSIX之前相比,这现在不像狂野西部那样,但是仍然没有理由喜欢echo -e。另请参阅stackoverflow.com/questions/11530203/…–
Tripleee

21
  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

6

单引号前面的$如下所示,但是双引号无效。

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

4

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

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

$STR2
EOF
)
echo "$NEWSTR"

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


2
好老的heredoc。而且可能也符合POSIX!
pyb

周围的报价$NEWSTRecho "$NEWSTR"是很重要的位置
沙迪

0

在我的系统(Ubuntu 17.10)上,无论从命令行(toto)键入还是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"

0

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

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

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

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

我知道这是两个动作,而不是一个,但我的缩进和可移植性OCD现在处于和平状态:)我最初开发此功能是为了能够拆分仅换行符分隔的输出,并且最终使用了\r用作终止字符的修改。即使对于以结尾的dos输出,这也使换行拆分工作\r\n

IFS="$(printf '\n\r')"

0

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

str=$(printf "%s" "first line")
str=$(printf "$str\n%s" "another line")
str=$(printf "$str\n%s" "and another line")

这是一种非常乏味的编写方式str=$(printf '%s\n' "first line" "another line" "and another line")(shell将方便地(或不方便地)从命令替换中修剪所有最后的换行符)。这里的几种答案已经printf以各种形式推广,因此我不确定这是否可以作为单独的答案增加任何价值。
重奏

它用于短字符串,但是如果您想保持整洁并且每行实际上是60个字符长,那么这是一种整齐的方法。考虑到这是我最终使用的解决方案,它确实为将来增加了一些东西,如果不是为您提供的,那么为我自己提供了。
亚当·K·迪恩

1
即使对于长字符串,我也喜欢printf '%s\n' "first line" \ (换行,缩进)'second line'等。另请参见有关printf -v str在不产生子shell的情况下分配变量的信息。
tripleee
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.