如何在 Bash 中将字符串拆分为多行?

Posted

技术标签:

【中文标题】如何在 Bash 中将字符串拆分为多行?【英文标题】:How to split strings over multiple lines in Bash? 【发布时间】:2011-11-11 01:55:54 【问题描述】:

如何将我的长字符串常量拆分为多行?

我知道你可以这样做:

echo "continuation \
lines"
>continuation lines

但是,如果您有缩进的代码,效果就不那么好了:

    echo "continuation \
    lines"
>continuation     lines

【问题讨论】:

Google 的 Bash Shell 样式指南 recommends "here" documents 用于“如果您必须编写超过 80 个字符的字符串”。见@tripleee's answer。 google.github.io/styleguide/… -- 这是新文档中的直接链接 答案清楚地表明了 bash 语言是多么有趣:-) 【参考方案1】:

这可能是你想要的

$       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

这将允许您以牺牲额外变量为代价获得干净的缩进代码。如果您将变量设为本地,那应该不会太糟糕。

【讨论】:

感谢您的帮助,虽然这确实删除了空格,但它们现在是单独的参数(Bash 将第二行的空格解释为参数分隔符)并且现在只能正确打印,因为回显命令。 哦,你想要一个(bash)字符串来跨越行!我现在明白了。 一个变量的解决方案:s="string no. 1" s+="string no. 2" s+=" string no. 3" echo "$s"【参考方案2】:

带有<<-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
____

(第二行前面有一个制表符和一个空格;制表符将在heredoc终止符之前由破折号运算符删除,而空格将被保留。)

对于将长而复杂的字符串包装在多行中,我喜欢printf

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

它也适用于您希望将重要的 shell 脚本片段嵌入另一种语言的环境中,而宿主语言的语法不允许您使用 here 文档,例如在 MakefileDockerfile 中。

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

【讨论】:

对我不起作用。 Ubuntu 16.04。我得到两行而不是预期的串联一行。 @PengheGeng 确实,这解决了消除压痕的问题,而不是连接线的问题。您仍然可以在行尾使用反斜杠换行符将两行连接在一起。 (但现在也请参见第一个 printf 示例。)【参考方案3】:

您可以使用bash arrays

$ str_array=("continuation"
             "lines")

然后

$ echo "$str_array[*]"
continuation lines

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

如果单词被双引号,$name[*] 扩展为单个单词 每个数组成员的值由第一个字符分隔 IFS 变量

所以设置IFS='' 以消除多余的空间

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

【讨论】:

不过你应该使用"$str_array[@]" 好收获!谢谢。对于echo,这无关紧要,但对于其他情况,它可能会产生巨大的差异***.com/questions/3348443/…【参考方案4】:

在某些情况下,利用 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 手册页的 PARAMETERS 部分:

名称=[值]...

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

【讨论】:

可能是此页面上最好的建议。使用 HEREDOC 和管道转换为 s 非常不直观,并且当字符串实际上需要离散放置的行分隔符时会失败。【参考方案5】:

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

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

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

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

这样做的好处是$message 可以完全用作字符串常量,而无需额外的空格或换行符。

请注意,上面的实际消息行每个都以tab 字符为前缀,此处文档本身将其剥离(因为使用了&lt;&lt;-)。最后还有换行符,然后换成dd加空格。

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

【讨论】:

【参考方案6】:

您可以根据缩进中的要求将其简单地用换行符(不使用反斜杠)分隔,如下所示,并且只是去除新行。

例子:

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

【讨论】:

【参考方案7】:

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

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.

瞧!

【讨论】:

echo -n 是可疑的;你应该改用printf @tripleee 究竟是如何使用GNU coreutil 可疑的,praytell?在这个简单的案例中,它完全符合预期,甚至允许终端正确包装输出。即使是 Google Shell Style Guide 在简单的情况下也使用它(尽管有对 HEREDOC 的建议)。这不是特殊格式的解决方案。 echo -n 的问题在于它不像您描述的那样可靠地工作。许多非 GNU 平台具有不同的 echo 实现,具有不同的特性。不过,只要您使用的是内置的 Bash,它就只是一种难闻的气味;但是,当可移植且可靠的解决方案实际上也更优雅时,为什么您更愿意降低您的解决方案的可移植性呢? 我在 Google 风格指南中找不到任何 echo -n 的示例。 @tripleee 一个问题是printf 有其自身相当大的可移植性问题。在 2021 年,抱怨 -n 标志删除了 echo 中的尾随 标志是一个非常精细的梳子。我什至认为它提供了更多的控制,而不是吐出任何行结尾。就个人而言,我更喜欢echo -ne "$outp",它也强制解释转义字符。几十年来严格兼容的建议是将cat 与HEREDOC 一起使用。但实际上,原始发帖人是在询问使用echo 并且问题标记为Bash【参考方案8】:

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

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

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

【讨论】:

【参考方案9】:

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

$: 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

【讨论】:

那么只需执行MY_VAR = $(echo x$) 即可。 IMO 这是最好、最简单的答案,以保持我的代码可读,每一行都是一个包含字符串本身的变量。我需要导出这个值,你不能导出数组。 @DKebler 您的评论包含多个语法错误。很难猜出你的意思,或者过于认真地对待周围的建议。另见useless use of echo.【参考方案10】:

但是,如果您有缩进的代码,效果就不那么好了:

    echo "continuation \
    lines"
>continuation     lines

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

    echo 'continuation' \
    'lines'
>continuation lines

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

【讨论】:

它适用于 echo 和 string 参数,但它不适用于其他东西,比如变量赋值。尽管问题与变量无关,但使用 echo 只是一个示例。如果你有x=,而不是echo ,你会得到错误:lines: command not found @Mr.LanceESloan 不过,这是因为没有加引号的空格,所以它实际上是 separate issue. 如果你删除反斜杠之前的空格,这也适用于分配。 Bash 是在浪费时间,这是肯定的。【参考方案11】:

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

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

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

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

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 中会更有意义,这样它就会在您的路径中。

查看 python 参考以了解有关 textwrap.dedent 工作原理的详细信息。

如果$'...'"$(...)" 的用法让您感到困惑,请再问一个问题(每个构造一个),如果还没有一个问题。最好提供您找到/提出的问题的链接,以便其他人获得链接参考。

【讨论】:

出于好意——甚至可能有用——尽管这可能是,OP 询问了关于基本 bash 语法的建议,你给了他一个 python 函数定义,它使用了 OO 范式、异常流控制和进口。此外,您调用了一个可执行文件作为字符串插值的一部分——问这种问题的人肯定不会在 bash 中看到。

以上是关于如何在 Bash 中将字符串拆分为多行?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 C / Objective-C 中将字符串文字拆分为多行?

如何在 BigQuery SQL 中将字符串列拆分为多行单个单词和单词对?

如何将包含字符“\ n”的多行字符串拆分为bash中的字符串数组? [复制]

在Bash中将字符串拆分为多个变量[重复]

如何在 bash shell 中将一个字符串拆分为多个字符串,至少用一个空格分隔?

在Oracle中将字符串拆分为多行