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

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

我知道你可以这样做:

echo "continuation \
lines"
>continuation lines

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

    echo "continuation \
    lines"
>continuation     lines

【问题讨论】:

标签: bash line indentation


【解决方案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

【讨论】:

【解决方案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 中看到。
        猜你喜欢
        • 2018-05-01
        • 1970-01-01
        • 2010-09-15
        • 1970-01-01
        • 2011-04-16
        相关资源
        最近更新 更多