如何在sh中的字符串中添加换行符?

时间:2010-06-09 13:00:22

标签: sh

STR="Hello\nWorld"
echo $STR

产生输出

Hello\nWorld

而不是

Hello
World

如何在字符串中添加换行符?

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

13 个答案:

答案 0 :(得分:282)

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

$ STR=$'Hello\nWorld'
$ echo "$STR"
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.

答案 1 :(得分:144)

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和BSD回声疯狂,一切都得到了整齐的打印和完全便携式支持C转义序列。现在每个人都要使用printf,永远不要回头。

答案 2 :(得分:48)

我根据其他答案做了什么

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

答案 3 :(得分:26)

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

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

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

或等效

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

答案 4 :(得分:19)

  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
    

答案 5 :(得分:11)

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

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

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

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

答案 6 :(得分:4)

在单引号之前的A $#... ... \ n ...&#39;如下所示,但双引号不起作用。

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

答案 7 :(得分:3)

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

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

$STR2
EOF
)
echo "$NEWSTR"

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

答案 8 :(得分:1)

免责声明:我首先写了这个,然后偶然发现了这个问题。我以为尚未发布该解决方案,因此看到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='
'

...但是它的健壮性较差,并且由于意外缩进代码或之后忘记对代码进行缩进而容易损坏,这使得在(缩进的)函数中使用不方便,而较早的解决方案则很健壮。

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

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.

答案 9 :(得分:0)

在我的系统(Ubuntu 17.10)上,您的示例只是在从命令行输入(到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){@ 1}}中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"

答案 10 :(得分:0)

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

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

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

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

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

\r\n

答案 11 :(得分:0)

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

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

答案 12 :(得分:0)

这不是理想的选择,但是我以类似于问题中使用的方法的方式编写了许多代码并定义了字符串。可接受的解决方案要求我重构很多代码,因此,我将每个fetch('https://api.covid19india.org/state_district_wise.json') .then(Response => Response.json()) .then(data => { Object.entries(data).forEach(([key, value]) => { console.log(value.statecode) }) })替换为\n,这对我来说很有效。