如何在 Bash 中分割多行字符串?

如何将长字符串常量分割为多行?

我知道你能做到:

echo "continuation \
lines"
>continuation lines

然而,如果你使用缩进的代码,效果就不太好了:

    echo "continuation \
lines"
>continuation     lines
204209 次浏览

这可能是你想要的

$       echo "continuation"\
>       "lines"
continuation lines

如果这会创建两个 echo 参数,而您只需要一个,那么让我们看看字符串串联。在 bash 中,将两个相邻的字符串连接起来:

$ echo "continuation""lines"
continuationlines

因此,连续行 没有缩进是分解字符串的一种方法:

$ echo "continuation"\
> "lines"
continuationlines

但使用缩进时:

$       echo "continuation"\
>       "lines"
continuation lines

你会得到两个参数,因为这不再是一个串联。

如果你想要一个单独的字符串跨越行,同时缩进但不能得到所有的空格,你可以尝试的一种方法是丢弃连续行并使用变量:

$ a="continuation"
$ b="lines"
$ echo $a$b
continuationlines

这将允许您以牺牲其他变量为代价获得清晰缩进的代码。如果你使变量局部化,这应该不会太糟糕。

这里使用 <<-HERE结束符的文档可以很好地处理缩进的多行文本字符串。它将从 here 文档中删除任何前导选项卡。(不过,线路终止符仍将保留。)

cat <<-____HERE
continuation
lines
____HERE

参见 http://ss64.com/bash/syntax-here.html

如果需要保留一些(但不是全部)前导空格,可以使用

sed 's/^  //' <<____HERE
This has four leading spaces.
Two of them will be removed by sed.
____HERE

或者使用 tr去掉换行:

tr -d '\012' <<-____
continuation
lines
____

(第二行在前面有一个制表符和一个空格; 制表符将在 herdoc 结束符之前被破折号操作符移除,而空格将被保留。)

对于在许多行上包装长的复杂字符串,我喜欢 printf:

printf '%s' \
"This will all be printed on a " \
"single line (because the format string " \
"doesn't specify any newline)"

它也可以很好地工作在这样的上下文中,即您希望在宿主语言的语法不允许您使用 here 文档的另一种语言中嵌入非常重要的 shell 脚本片段,例如在 MakefileDockerfile中。

printf '%s\n' >./myscript \
'#!/bin/sh` \
"echo \"G'day, World\"" \
'date +%F\ %T' && \
chmod a+x ./myscript && \
./myscript

这可能并没有真正回答你的问题,但是无论如何你会发现它是有用的。

第一个命令创建由第二个命令显示的脚本。

第三个命令使该脚本可执行。

第四个命令提供了一个用法示例。

john@malkovich:~/tmp/so$ echo $'#!/usr/bin/env python\nimport textwrap, sys\n\ndef bash_dedent(text):\n    """Dedent all but the first line in the passed `text`."""\n    try:\n        first, rest = text.split("\\n", 1)\n        return "\\n".join([first, textwrap.dedent(rest)])\n    except ValueError:\n        return text  # single-line string\n\nprint bash_dedent(sys.argv[1])'  > bash_dedent
john@malkovich:~/tmp/so$ cat bash_dedent
#!/usr/bin/env python
import textwrap, sys


def bash_dedent(text):
"""Dedent all but the first line in the passed `text`."""
try:
first, rest = text.split("\n", 1)
return "\n".join([first, textwrap.dedent(rest)])
except ValueError:
return text  # single-line string


print bash_dedent(sys.argv[1])
john@malkovich:~/tmp/so$ chmod a+x bash_dedent
john@malkovich:~/tmp/so$ echo "$(./bash_dedent "first line
>     second line
>     third line")"
first line
second line
third line

请注意,如果您真的想要使用这个脚本,那么将可执行脚本移动到 ~/bin中以使其位于您的路径中会更有意义。

有关 textwrap.dedent如何工作的详细信息,请查看 python 参考资料。

如果 $'...'"$(...)"的用法让您感到困惑,那么如果还没有提出另一个问题(每个构造一个) ,请提出另一个问题。提供一个你发现/问的问题的链接,这样其他人就会有一个链接的参考资料。

我遇到过这样一种情况: 我必须发送一条长消息作为命令参数的一部分,并且必须遵守行长限制。命令看起来像这样:

somecommand --message="I am a long message" args

我解决这个问题的方法是将消息作为 here 文档移出(像@tripleee 建议的那样)。但是 here 文档变成了 stdin,所以需要重新读入,我采用了以下方法:

message=$(
tr "\n" " " <<-END
This is a
long message
END
)
somecommand --message="$message" args

这样做的好处是,$message可以准确地用作字符串常量,不需要额外的空格或换行符。

请注意,上面的实际消息行每行前面都有一个 tab字符,这是由这里的文档本身剥离的(因为使用了 <<-)。在结尾仍然有断行符,然后用带空格的 tr代替。

还要注意,如果不删除换行符,它们将在展开 "$message"时显示。在某些情况下,您可以通过删除 $message周围的双引号来解决问题,但是消息将不再是单个参数。

然而,如果你使用缩进的代码,效果就不太好了:

    echo "continuation \
lines"
>continuation     lines

尝试使用单引号并连接字符串:

    echo 'continuation' \
'lines'
>continuation lines

注意: 连接包括一个空格。

你可以使用 Bash 数组

$ str_array=("continuation"
"lines")

那么

$ echo "${str_array[*]}"
continuation lines

有一个额外的空间,因为(在 bash 手册之后) :

如果该单词是双引号,则 ${name[*]}扩展为具有 属性的第一个字符分隔的每个数组成员的值 IFS 变量

因此,将 IFS=''设置为去除额外的空间

$ IFS=''
$ echo "${str_array[*]}"
continuationlines

您可以像下面这样在缩进中使用换行符(不使用反斜杠)简单地将其分隔开来,只需去掉新行即可。

例如:

echo "continuation
of
lines" | tr '\n' ' '

或者,如果它是一个变量定义换行将自动转换为空格。因此,只有在适用的情况下才可以使用额外的空格。

x="continuation
of multiple
lines"
y="red|blue|
green|yellow"


echo $x # This will do as the converted space actually is meaningful
echo $y | tr -d ' ' # Stripping of space may be preferable in this case

这并不完全符合用户的要求,但是创建跨多行的长字符串的另一种方法是逐步构建它,如下所示:

$ greeting="Hello"
$ greeting="$greeting, World"
$ echo $greeting
Hello, World

显然,在这种情况下,一次性构建它会更简单,但是这种风格在处理较长的字符串时可能非常轻量级和易于理解。

根据您将接受的风险类型以及您对数据的了解和信任程度,您可以使用简单变量插值。

$: x="
this
is
variably indented
stuff
"
$: echo "$x" # preserves the newlines and spacing


this
is
variably indented
stuff


$: echo $x # no quotes, stacks it "neatly" with minimal spacing
this is variably indented stuff

在某些场景中,利用 Bash 的连接能力可能是合适的。

例如:

temp='this string is very long '
temp+='so I will separate it onto multiple lines'
echo $temp
this string is very long so I will separate it onto multiple lines

来自 Bash Man 页面的“参数”部分:

Name = [ value ] ...

... 在赋值语句为 shell 变量或数组索引赋值的上下文中,可以使用 + = 运算符附加或添加变量的前一个值。当将 + = 应用于为其设置了整数属性的变量时,value 将作为算术表达式进行计算,并添加到变量的当前值中,该值也将进行计算。当使用复合赋值(参见下面的数组)将 + = 应用于数组变量时,变量的值不会取消设置(使用 = 时就是这样) ,并且新的值将从大于数组最大索引(对于索引数组)的一个值开始附加到数组,或者作为额外的键值对添加到关联数组中。当应用于字符串值变量时,将展开 value 并将其追加到变量的值中。

行的延续也可以通过巧妙地使用语法来实现。

echo为例:

# echo '-n' flag prevents trailing <CR>
echo -n "This is my one-line statement" ;
echo -n " that I would like to make."
This is my one-line statement that I would like to make.

在 vars 的情况下:

outp="This is my one-line statement" ;
outp+=" that I would like to make." ;
echo -n "${outp}"
This is my one-line statement that I would like to make.

Vars 的另一种方法:

outp="This is my one-line statement" ;
outp="${outp} that I would like to make." ;
echo -n "${outp}"
This is my one-line statement that I would like to make.

瞧!

以下是@tripleee 的 printf示例(+ 1) :

LONG_STRING=$( printf '%s' \
'This is the string that never ends.' \
' Yes, it goes on and on, my friends.' \
' My brother started typing it not knowing what it was;' \
" and he'll continue typing it forever just because..." \
' (REPEAT)' )


echo $LONG_STRING


This is the string that never ends. Yes, it goes on and on, my friends. My brother started typing it not knowing what it was; and he'll continue typing it forever just because... (REPEAT)

我们已经在句子之间加入了明确的空格,例如“ ' Yes...”。同样,如果我们可以不使用变量:

echo "$( printf '%s' \
'This is the string that never ends.' \
' Yes, it goes on and on, my friends.' \
' My brother started typing it not knowing what it was;' \
" and he'll continue typing it forever just because..." \
' (REPEAT)' )"


This is the string that never ends. Yes, it goes on and on, my friends. My brother started typing it not knowing what it was; and he'll continue typing it forever just because... (REPEAT)

对这首永无止境的歌的感谢