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

发布于 2024-09-05 03:01:20 字数 349 浏览 5 评论 0原文

STR="Hello\nWorld"
echo $STR

会生成输出

Hello\nWorld

而不是

Hello
World

What should do to have a newline in a string?

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

This

STR="Hello\nWorld"
echo $STR

produces as output

Hello\nWorld

instead of

Hello
World

What should I do to have a newline in a string?

Note: This question is not about echo.
I'm aware of echo -e, but I'm looking for a solution that allows passing a string (which includes a newline) as an argument to other commands that do not have a similar option to interpret \n's as newlines.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(13

是伱的 2024-09-12 03:01:20

如果您使用的是 Bash,则可以在特别引用的 $'string' 内使用反斜杠转义符。例如,添加 \n

STR=

输出:

Hello
World

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

STR='Hello
World'

Bash 可识别 < 中的许多其他反斜杠转义序列代码>$'' 字符串。以下是 Bash 手册页的摘录:

Words of the form 
Hello\nWorld'
echo "$STR" # quotes are required here!

输出:


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


Bash 可识别 < 中的许多其他反斜杠转义序列代码>$'' 字符串。以下是 Bash 手册页的摘录:


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.
Hello\nWorld' echo "$STR" # quotes are required here!

输出:

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

Bash 可识别 < 中的许多其他反斜杠转义序列代码>$'' 字符串。以下是 Bash 手册页的摘录:

If you're using Bash, you can use backslash-escapes inside of a specially-quoted $'string'. For example, adding \n:

STR=

Prints:

Hello
World

If you're using pretty much any other shell, just insert the newline as-is in the string:

STR='Hello
World'

Bash recognizes a number of other backslash escape sequences in the $'' string. Here is an excerpt from the Bash manual page:

Words of the form 
Hello\nWorld'
echo "$STR" # quotes are required here!

Prints:


If you're using pretty much any other shell, just insert the newline as-is in the string:


Bash recognizes a number of other backslash escape sequences in the $'' string. Here is an excerpt from the Bash manual page:


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 (
quot;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.
Hello\nWorld' echo "$STR" # quotes are required here!

Prints:

If you're using pretty much any other shell, just insert the newline as-is in the string:

Bash recognizes a number of other backslash escape sequences in the $'' string. Here is an excerpt from the Bash manual page:

梦亿 2024-09-12 03:01:20

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 来满足您的所有输出需求,并且永远不要回头。

Echo is so nineties and so fraught with perils that its use should result in core dumps no less than 4GB. Seriously, echo's problems were the reason why the Unix Standardization process finally invented the printf utility, doing away with all the problems.

So to get a newline in a string, there are two ways:

# 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"

There! No SYSV vs BSD echo madness, everything gets neatly printed and fully portable support for C escape sequences. Everybody please use printf now for all your output needs and never look back.

几味少女 2024-09-12 03:01:20

我根据其他答案所做的是

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

What I did based on the other answers was

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
不甘平庸 2024-09-12 03:01:20

我发现 -e 标志优雅且直接

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

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

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

I find the -e flag elegant and straight forward

bash$ STR="Hello\nWorld"

bash$ echo -e $STR
Hello
World

If the string is the output of another command, I just use quotes

indexes_diff=$(git diff index.yaml)
echo "$indexes_diff"
反话 2024-09-12 03:01:20

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

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

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

或等效的

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

The problem isn't with the shell. The problem is actually with the echo command itself, and the lack of double quotes around the variable interpolation. You can try using echo -e but that isn't supported on all platforms, and one of the reasons printf is now recommended for portability.

You can also try and insert the newline directly into your shell script (if a script is what you're writing) so it looks like...

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

or equivalently

#!/bin/sh
string="Hello
World"
echo "$string"  # note double quotes!
贱贱哒 2024-09-12 03:01:20
  1. 唯一简单的替代方法是在变量中实际键入新行:

    $ STR='新
    线'
    $ printf '%s' "$STR"
    新的
    线
    

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

  1. 有几个与换行字符等效的字符。

    \n ### 表示换行符的常用方法。
    \012 ### 新行字符的八进制值。
    \x0A ### 新行字符的十六进制值。
    

但所有这些都需要某种工具的“解释”(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
  1. 在某些 shell 中,序列 $' 是一种特殊的 shell 扩展。
    已知可在 ksh93、bash 和 zsh 中工作:

    $ STR=
    
  2. 当然,更复杂的解决方案也是可能的:

    $ echo '6e65770a6c696e650a' | xxd-p-r
    新的
    线
    

或者

    $ echo "new line" | sed 's/ \+/\n/g'
    new
    line
新\n行'
  • 当然,更复杂的解决方案也是可能的:

  • 或者

    1. The only simple alternative is to actually type a new line in the variable:

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

    Yes, that means writing Enter where needed in the code.

    1. There are several equivalents to a new line character.

      \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.
      

    But all those require "an interpretation" by some tool (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.
    

    And therefore, the tool is required to build a string with a new-line:

        $ STR="$(printf 'new\nline')"
        $ printf '%s' "$STR"
        new
        line
    
    1. In some shells, the sequence $' is a special shell expansion.
      Known to work in ksh93, bash and zsh:

      $ STR=
      
    2. Of course, more complex solutions are also possible:

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

    Or

        $ echo "new line" | sed 's/ \+/\n/g'
        new
        line
    
    new\nline'
  • Of course, more complex solutions are also possible:

  • Or

    你穿错了嫁妆 2024-09-12 03:01:20

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

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

    A $ right before single quotation marks '...\n...' as follows, however double quotation marks doesn't work.

    $ echo 
    Hello\nWorld'
    Hello
    World
    $ echo $"Hello\nWorld"
    Hello\nWorld
    
    明明#如月 2024-09-12 03:01:20

    免责声明:我首先写了这篇文章,然后偶然发现了这个问题。我认为这个解决方案尚未发布,并看到 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 会遇到麻烦,因为 dash 会出现问题否则会丢失“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.
    

    Disclaimer: I first wrote this and then stumbled upon this question. I thought this solution wasn't yet posted, and saw that tlwhitec did post a similar answer. Still I'm posting this because I hope it's a useful and thorough explanation.

    Short answer:

    This seems quite a portable solution, as it works on quite some shells (see comment).
    This way you can get a real newline into a variable.

    The benefit of this solution is that you don't have to use newlines in your source code, so you can indent
    your code any way you want, and the solution still works. This makes it robust. It's also portable.

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

    Longer answer:

    Explanation of the above solution:

    The newline would normally be lost due to command substitution, but to prevent that, we add a 'q' and remove it afterwards. (The reason for the double quotes is explained further below.)

    We can prove that the variable contains an actual newline character (0x0A):

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

    (Note that the '%s' was needed, otherwise printf will translate a literal '\n' string into an actual 0x0A character, meaning we would prove nothing.)

    Of course, instead of the solution proposed in this answer, one could use this as well (but...):

    nl='
    '
    

    ... but that's less robust and can be easily damaged by accidentally indenting the code, or by forgetting to outdent it afterwards, which makes it inconvenient to use in (indented) functions, whereas the earlier solution is robust.

    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}
    }
    

    Practical example of the above solution:

    # 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.
    
    笛声青案梦长安 2024-09-12 03:01:20

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

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

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

    I'm no bash expert, but this one worked for me:

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

    I found this easier to formatting the texts.

    为你拒绝所有暧昧 2024-09-12 03:01:20

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

    This isn't ideal, but I had written a lot of code and defined strings in a way similar to the method used in the question. The accepted solution required me to refactor a lot of the code so instead, I replaced every \n with "$'\n'" and this worked for me.

    零度° 2024-09-12 03:01:20

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

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

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

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

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

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

    Those picky ones that need just the newline and despise the multiline code that breaks indentation, could do:

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

    Bash (and likely other shells) gobble all the trailing newlines after command substitution, so you need to end the printf string with a non-newline character and delete it afterwards. This can also easily become a oneliner.

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

    I know this is two actions instead of one, but my indentation and portability OCD is at peace now :) I originally developed this to be able to split newline-only separated output and I ended up using a modification that uses \r as the terminating character. That makes the newline splitting work even for the dos output ending with \r\n.

    IFS="$(printf '\n\r')"
    
    勿忘初心 2024-09-12 03:01:20

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

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

    I wasn't really happy with any of the options here. This is what worked for me.

    str=$(printf "%s" "first line")
    str=$(printf "$str\n%s" "another line")
    str=$(printf "$str\n%s" "and another line")
    
    胡渣熟男 2024-09-12 03:01:20

    在我的系统(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
    

    我猜是这样回答你的问题:它确实有效。 (我没有试图弄清楚细节,例如 sh\n 的换行符替换究竟在什么时候发生)。

    但是,我注意到,使用 bash 执行相同的脚本时,其行为会有所不同,并且会打印出 Hello\nWorld

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

    我已经设法使用 bash 获取所需的输出,如下所示:

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

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

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

    [bash]§ echo "Hello
    > World"
    

    On my system (Ubuntu 17.10) your example just works as desired, both when typed from the command line (into sh) and when executed as a sh script:

    [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
    

    I guess this answers your question: it just works. (I have not tried to figure out details such as at what moment exactly the substitution of the newline character for \n happens in sh).

    However, i noticed that this same script would behave differently when executed with bash and would print out Hello\nWorld instead:

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

    I've managed to get the desired output with bash as follows:

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

    Note the double quotes around $STR. This behaves identically if saved and run as a bash script.

    The following also gives the desired output:

    [bash]§ echo "Hello
    > World"
    
    ~没有更多了~
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文