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


409

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

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,但是多余的空格怎么来?


2
我不确定,但是..如果您只键入text="this is line one\nthis is line two\nthis is line three"同一行..?(不输入任何内容)
Yohanes Khosiawan许先汉2014年

4
删除\n每行上的,您已经点击了换行符以移至新行
Mark Setchell,2014年

您已经给出了\n。那么为什么要将下一行放在换行中?简而言之text="this is line one\nthis is line two\nthis is line three"
Jayesh Bhoi 2014年

1
删除\n每行末尾的会导致输出全部在一行上一起运行。
乔纳森·哈特利

18
阿哈:"$text"在回声行中的双引号周围至关重要。没有它们,所有换行符(立即数和'\ n')均无效。有了他们,他们都会做。
乔纳森·哈特利

Answers:


662

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

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

之后的字符串<<指示在哪里停止。

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

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"

更好的是,您可以使用缩进使其在代码中更加突出。这次只需添加一个-后缀<<即可停止标签的显示。

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

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


38
为什么在第二和第五代码块的第一行中具有<<-而不是<<?-有什么特别的吗?
lohfu

35
@ sup3rman'-'忽略前导标签。见stackoverflow.com/questions/2500436/...
kervin

7
如何使状态为0的读取退出?似乎找不到行尾(因为它是-d”),并以1退出,这在您使用脚本时无济于事。
Misha Slyusarev

6
@MishaSlyusarev使用-d'\ 0'并在最后一行的末尾添加\ 0
Lars Schneider

11
在中使用-d选项read -r -d '' VARIABLE <<- EOM对我不起作用。
dron22 '16

186

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

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

如果您使用制表符(例如'\ t')缩进字符串,则缩进将被删除。如果缩进空格,则缩进将保留。

注:这显著的最后一个右括号是另一条线路上。该END文本必须出现在单独一行。


1
那有意义吗?)在同一行的Mac上均可使用。我认为这是因为介于两者之间$(并且)将在其自己的宇宙中执行,并且实际的命令无论如何都不会看到')'。我想知道它是否也适用于其他人。
deej

不同的外壳可能会以不同的方式解释事物。在bash文档中,似乎没有一种或另一种方式说什么,但所有示例均以单独的方式讲。
Andrew Miner

有趣的是,我也尝试为USAGE变量设置值时发现了答案。因此,您的答案完全匹配。:)
Bunyk '18

1
@deej从POSIX规范开始此处文档…一直持续到一行仅包含定界符和<newline>
Fornost

1
@Fornost它与我说的并不矛盾
deej

84

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

1
感谢您解释为什么mod在使用“ echo”命令时会看到额外的空间。在bash脚本中使用时(与交互式shell会话相对),此答案也很好用。觉得这是真正的答案,应该是一个可以接受的答案。
onelaview

1
这应该被接受。所有其他答案都提供了许多有趣的其他方法来解决OP的痛苦,但从技术上讲,问题是“多余的空间如何产生”,没有人解决这个问题:-)!谢谢Josh提出-1的谜团!
德米特里Shevkoplyas

45

在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

3
@Macindows我似乎忘记了的-e选择echo。请再试一遍。
克里斯·梅斯

41

自从我希望正确缩进每一行以来,我发现了更多解决方案:

  1. 您可以使用echo

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

    如果您"\n"\在行尾放置在前面,则此方法不起作用。

  2. 另外,您可以使用printf来提高可移植性(我碰巧遇到了很多问题echo):

    printf '%s\n' \
        "this is line one"   \
        "this is line two"   \
        "this is line three" \
        > filename
  3. 另一个解决方案可能是:

    text=''
    text="${text}this is line one\n"
    text="${text}this is line two\n"
    text="${text}this 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
  4. 另一种解决方案是通过混合printfsed

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

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

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

    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更具可读性。当我不在乎时,只需将引号中的字符串保持打开状态即可。
Pysis

非常好的答案,尤其是3b
Sumit Trehan

“如果将“ \ n”放在行末\之前,则不起作用。” - 在使用echo时是一个很好的技巧
Noam Manos

6

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

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

String1
String2
String3
...
StringN
_______________________________________________________________________________

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


0

仅提及简单的单行连接,因为有时它会很有用。

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

你会得到这样的东西

酒吧 
   富 
 古加 
   uga 

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

echo "$v3" > filename

0

有很多方法可以做到这一点。对我来说,将缩进的字符串管道插入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的答案,但略有改进。


-1

如下所示,它将起作用:

AA='first line
\nsecond line 
\nthird line'
echo $AA
output:
first line
second line
third line
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.