如何在 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 文档,例如在 Makefile
或 Dockerfile
中。
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 和管道转换为我遇到了一种情况,我必须发送一条长消息作为命令参数的一部分,并且必须遵守行长限制。命令看起来像这样:
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
字符为前缀,此处文档本身将其剥离(因为使用了<<-
)。最后还有换行符,然后换成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
。这并不是用户所要求的,但是创建跨越多行的长字符串的另一种方法是逐步构建它,如下所示:
$ 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中的字符串数组? [复制]