带有额外空格的多行字符串(保留缩进)

Posted

技术标签:

【中文标题】带有额外空格的多行字符串(保留缩进)【英文标题】:Multi-line string with extra space (preserved indentation) 【发布时间】:2014-07-18 17:11:46 【问题描述】:

我想用以下内容将一些预定义的文本写入文件:

text="this is line one\n
this is line two\n
this is line three"

echo -e $text > filename

我期待这样的事情:

this is line one
this is line two
this is line three

但是得到了这个:

this is line one
 this is line two
 this is line three

我肯定每个\n后面都没有空格,但是多余的空格是怎么出来的呢?

【问题讨论】:

我不确定但是.. 如果您只是在同一行中输入text="this is line one\nthis is line two\nthis is line three" 会怎样..? (没有任何输入) 去掉每一行的\n,你已经打了换行符移动到新行 你已经给了\n。那么为什么你把下一行换成新行呢?只需text="this is line one\nthis is line two\nthis is line three" 删除每行末尾的\n 会导致输出全部在一行上运行。 啊哈:在 echo 行中的 "$text" 周围加上双引号至关重要。没有它们,任何换行符(文字和'\n')都不起作用。有了他们,他们都可以。 【参考方案1】:

难以阅读:

This 看起来太 bash 了 :) (难以阅读)我的一般口味:

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

更好,更易于阅读:

让我们来点 Pythonic(这仍然是 bash):

text="this is line one
      this is line two
      this is line three\n"
dedent text
printf "$text"             # print to screen
printf "$text" > file.txt  # print to a file

啊……这样更好。 :) 这让我想起了 Python 的 textwrap.dedent() 函数 which I use here。

下面是神奇的dedent 函数的样子:

dedent() 
    local -n reference="$1"
    reference="$(echo "$reference" | sed 's/^[[:space:]]*//')"

样本输出到屏幕:

this is line one
this is line two
this is line three

如果不首先调用dedent text,输出将如下所示:

this is line one
      this is line two
      this is line three

变量text被传递给dedent通过引用,这样内部函数修改的内容会影响外部变量函数。

更多细节和解释和参考,在这里查看我的另一个答案:Equivalent of python's textwrap dedent in bash

你最初的尝试有问题​​

OP 的引用(加上我的重点):

我肯定每个\n 后面都没有空格,但是多余的空格是怎么出来的?

你最初的尝试是这样的:

text="this is line one\n
this is line two\n
this is line three"
echo -e $text

...但是您的输出在第 2 行和第 3 行之前有一个额外的空间。为什么?

通过推理和实验,我的结论是echo 将行尾的实际换行符(您实际按下Enter时得到的换行符)转换为空格。因此,该空格会显示在文本中每个 \n 之后 行之前。

因此,解决方案是在每行末尾添加一个反斜杠 \ 在字符串引号内的任何行末尾处转义真正的换行符,如下所示:

text="this is line one\n\
this is line two\n\
this is line three"

echo -e "$text"

不要在后面的反斜杠之前放置一个空格(例如:text="this is line one\n \),否则该空格将直接返回到您的输出中,并导致您在多余的空格上出现同样的问题! p>

或者,只需将我的技术与上面的 dedent 函数一起使用,它还具有能够缩进代码的附加功能,看起来非常漂亮、美观和可读。

【讨论】:

【参考方案2】:

或者使用空格缩进文本:

#!/bin/sh

sed 's/^[[:blank:]]*//' >filename <<EOF
    this is line one
    this is line two
    this is line three
EOF

相同但使用变量:

#!/bin/sh

text="$(sed 's/^[[:blank:]]*//' << whatever
    this is line one
    this is line two
    this is line three
)"

echo "$text" > filename

;-)

【讨论】:

【参考方案3】:

我听说正在寻找这个答案,但也想将它传递给另一个命令。给定的答案是正确的,但如果有人想管道它,你需要像这样在多行字符串之前管道它

echo | tee /tmp/pipetest << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

这将允许您拥有多行字符串,但也可以将其放入后续命令的标准输入中。

【讨论】:

【参考方案4】:

有很多方法可以做到这一点。对我来说,将缩进的字符串输送到 sed 效果很好。

printf_strip_indent() 
   printf "%s" "$1" | sed "s/^\s*//g" 


printf_strip_indent "this is line one
this is line two
this is line three" > "file.txt"

此答案基于 Mateusz Piotrowski 的答案,但有所改进。

【讨论】:

【参考方案5】:

Heredoc 听起来更方便。它用于将多个命令发送到命令解释程序,如 excat

cat << EndOfMessage
This is line 1.
This is line 2.
Line 3.
EndOfMessage

&lt;&lt; 后面的字符串表示停止位置。

要将这些行发送到文件,请使用:

cat > $FILE <<- EOM
Line 1.
Line 2.
EOM

您也可以将这些行存储到一个变量中:

read -r -d '' VAR << EOM
This is line 1.
This is line 2.
Line 3.
EOM

这会将行存储到名为 VAR 的变量中。

打印时,请记住变量周围的引号,否则您将看不到换行符。

echo "$VAR"

更好的是,您可以使用缩进使其在您的代码中更加突出。这次只需在&lt;&lt; 之后添加- 即可阻止标签出现。

read -r -d '' VAR <<- EOM
    This is line 1.
    This is line 2.
    Line 3.
EOM

但是你必须在代码中使用制表符而不是空格来缩进。

【讨论】:

为什么在第 2 和第 5 个代码块的第一行有 @sup3rman '-' 忽略前导制表符。见***.com/questions/2500436/… 如何让状态为 0 的读取退出?似乎找不到行尾(因为它是 -d '')并以 1 退出,这在您使用脚本时无济于事。 @MishaSlyusarev 使用 -d '\0' 并在最后一行的末尾添加一个 \0 read -r -d '' VARIABLE &lt;&lt;- EOM 中使用-d 选项对我不起作用。【参考方案6】:

只是提到一个简单的单行连接,因为它有时很有用。

# for bash

v=" guga "$'\n'"   puga "

# Just for an example.
v2="bar "$'\n'"   foo "$'\n'"$v"

# Let's simplify the previous version of $v2.
n=$'\n'
v3="bar $n   foo $n$v"

echo "$v3" 

你会得到这样的东西

酒吧 富 古加 普加

所有开头和结尾的空格都将保留

echo "$v3" > filename

【讨论】:

【参考方案7】:

在 bash 脚本中,以下工作:

#!/bin/sh

text="this is line one\nthis is line two\nthis is line three"
echo -e $text > filename

或者:

text="this is line one
this is line two
this is line three"
echo "$text" > filename

cat 文件名给出:

this is line one
this is line two
this is line three

【讨论】:

似乎替代技巧在 Shell 脚本中有效,但似乎不适用于 bash。 @Macindows 我似乎忘记了echo-e 选项。请重试。【参考方案8】:

我找到了更多解决方案,因为我想让每一行都正确缩进:

    你可以使用echo:

    echo    "this is line one"   \
        "\n""this is line two"   \
        "\n""this is line three" \
        > filename
    

    如果您将"\n" 放在行尾的\ 之前,则不起作用。

    或者,您可以使用printf 以获得更好的可移植性(我碰巧使用echo 有很多问题):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
    

    另一种解决方案可能是:

    text=''
    text="$textthis is line one\n"
    text="$textthis is line two\n"
    text="$textthis is line three\n"
    printf "%b" "$text" > filename
    

    text=''
    text+="this is line one\n"
    text+="this is line two\n"
    text+="this is line three\n"
    printf "%b" "$text" > filename
    

    另一种解决方案是通过混合printfsed 来实现。

    if something
    then
        printf '%s' '
        this is line one
        this is line two
        this is line three
        ' | sed '1d;$d;s/^    //g'
    fi
    

    当您将缩进级别硬编码到代码中时,重构这样格式化的代码并不容易。

    可以使用辅助函数和一些变量替换技巧:

    unset text
    _()  text="$text$text+
    $*"; 
    # That's an empty line which demonstrates the reasoning behind 
    # the usage of "+" instead of ":+" in the variable substitution 
    # above.
    _ ""
    _ "this is line one"
    _ "this is line two"
    _ "this is line three"
    unset -f _
    printf '%s' "$text"
    

【讨论】:

3b 是我想要独立保留代码缩进和字符串缩进的首选解决方案,至少当我不能在变量赋值中使用 2 并且比 3a 更具可读性时。当我不在乎时,我只是在几行中保持带引号的字符串打开。 非常好的答案,尤其是 3b "如果你在行尾的 \ 前面加上 "\n" 是行不通的。" - 在使用 echo 时是一个很好的提示 printf 将我保存在一个简单的 docker 容器中,不需要 \n 真的很好【参考方案9】:

如果你把它放在下面,它会起作用:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line

【讨论】:

【参考方案10】:

以下是我将多行字符串分配给变量的首选方式(我认为它看起来不错)。

read -r -d '' my_variable << \
_______________________________________________________________________________

String1
String2
String3
...
StringN
_______________________________________________________________________________

两种情况下下划线的数量相同(此处为 80)。

【讨论】:

这很漂亮。谢谢!【参考方案11】:

如果您试图将字符串放入变量中,另一种简单的方法是这样的:

USAGE=$(cat <<-END
    This is line one.
    This is line two.
    This is line three.
END
)

如果你用制表符缩进你的字符串(即'\t'),缩进将被去除。如果你用空格缩进,缩进会留在里面。

注意: 重要的是,最后一个右括号位于另一行。 END 文本必须单独出现在一行中。

【讨论】:

这很重要吗?在我的 Mac 上工作,) 在同一行。我认为这是因为$() 之间的内容将在它自己的宇宙中执行,而实际的命令无论如何都不会看到')'。我想知道它是否也适用于其他人。 不同的shell可能会以不同的方式解释事物。在bash documentation 中,它似乎并没有以一种或另一种方式说明任何事情,但所有的例子都有它自己的一行。 有趣的是,我发现你在尝试为 USAGE 变量设置值时也回答了。所以你的答案完全匹配。 :) @deej 来自POSIX spec:here-document … 一直持续到有一行只包含分隔符和 @Fornost 这与我所说的并不矛盾【参考方案12】:

echo 在传递给它的参数之间添加空格。 $text 是可变扩展和分词的,所以你的echo 命令相当于:

echo -e "this" "is" "line" "one\n" "this" "is" "line" "two\n"  ...

可以看到“this”前面会加一个空格。您可以删除换行符,并引用 $text 以保留换行符:

text="this is line one
this is line two
this is line three"

echo "$text" > filename

或者你可以使用printf,它比echo更健壮和便携:

printf "%s\n" "this is line one" "this is line two" "this is line three" > filename

在支持大括号扩展的bash 中,您甚至可以这样做:

printf "%s\n" "this is line "one,two,three > filename

【讨论】:

感谢您解释为什么 mod 在使用“echo”命令时会看到额外的空间。在 bash 脚本中使用时,这个答案也很有效(与交互式 shell 会话相反)。感觉这是真正的答案,应该是公认的答案。 这应该是被接受的答案。所有其他答案都提供了大量有趣的 other 方法来解决 OP 的痛苦,但从技术上讲,问题是“额外的空间是如何产生的”,没有人解决这个问题 :-) !!!谢谢你,乔希让 -1 变得神秘!

以上是关于带有额外空格的多行字符串(保留缩进)的主要内容,如果未能解决你的问题,请参考以下文章

ES6的多行字符串

简明python笔记

多行 CSS 注释是不是有最佳实践? [关闭]

显示带有不间断空格和多行的 NSAttributedString 的 Swift 错误

如何用空格或缩进注释多行

python入门 python字符串换行显示字符串太长连接多行