缩进后如何在多行中为变量分配字符串值?


54

问题:

  1. 我需要给一个变量赋一个相当长的值。
  2. 我脚本的所有行都必须在一定数量的列下。

因此,我尝试使用多个行来分配它。

没有缩进就很简单:

VAR="This displays without \
any issues."
echo "${VAR}"

结果:

This displays without any issues.

但是缩进:

    VAR="This displays with \
    extra spaces."
    echo "${VAR}"

结果:

This displays with      extra spaces.

如何在没有这些空格的情况下优雅地分配它?

Answers:


30

这里的问题是,您用双引号(“”)包围了变量。删除它,一切正常。

    VAR="This displays with \
    extra spaces."
    echo ${VAR}

输出量

 This displays with extra spaces.

这里的问题是,双引号变量会保留所有空白字符。如果您明确需要它,可以使用它。

例如,

$ echo "Hello     World    ........ ...            ...."

将打印

Hello     World    ........ ...            ....

在删除引号时,其不同之处

$ echo Hello     World    ........ ...            ....
Hello World ........ ... ....

此处Bash删除了文本中的多余空格,因为在第一种情况下,整个文本被视为“单个”参数,因此保留了多余的空格。但是在第二种情况下,echo命令会将文本作为5个参数接收。

在将参数传递给命令时,引用变量也将很有帮助。

在以下命令中,echo仅获取单个参数为"Hello World"

$ variable="Hello World"
$ echo "$variable"

但是在以下情况下,echo有两个参数为HelloWorld

$ variable="Hello World"
$ echo $variable

大多数答案都效果很好,但这是最简单的。谢谢!
Sman865

12
@ Sman865-当我告诉您这实际上绝对是这里提供的最复杂的答案时,请相信我,并且在大多数方面(尤其是在其开幕式声明中)这也是错误的。围绕价值分配的任何问题都不能与其以后的扩展相关—只是倒退。很抱歉,坎南,但是这个答案既错误又错误。现场拆分扩展$IFS是一个强大而通用的工具-但是以这种方式编写的代码永远不会产生任何可靠的结果。
mikeserv

2
从文件名扩展(任何一个* ? [])开始,扩展变量时不使用引号或早或晚会引起问题。
spuratic先生2014年

我同意变量必须用双引号引起来以防止bash扩展。但是对于特殊情况,我们可以避免使用它以防止代码复杂化。
Kannan Mohan 2014年

1
我同意@mikeserv,如果以其面值来考虑,这是一个非常糟糕的建议。如果使用而没有后果,则将损坏。例如,如果将变量作为参数传递给命令,则您不希望将每个单词分解为单独的参数。
haridsv

28

esuoxuMickaëlBucas提供的解决方案是执行此操作的常见且更便携式的方法。

这里有一些bash解决方案(其中一些也可以在其他shell中使用,例如zsh)。首先,使用+=append运算符(对于整数变量,常规变量和数组,每种运算符的工作方式略有不同)。

text="Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
text+="tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
text+="quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." 

如果要在文本中使用换行符(或其他空格/转义符),请使用$''引号代替:

text=$'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n'
text+=$'...'

接下来,使用printf -v将格式化的值分配给变量

printf -v text "%s" "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed " \
                    "do eiusmod empor incididunt ut labore et dolore magna aliqua. "\
                    "Ut enim ad minim veniam ..."

这里的技巧是参数比格式说明符更多,因此与大多数printf函数不同,bash会重复使用格式字符串,直到用完为止。您可以\n在格式字符串中放入a ,或使用$''(或同时使用两者)处理空格。

接下来,使用数组:

text=("Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod "
      "tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, "
      "quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ..." )

您还可以+=用来逐行构建文本(请注意()):

text+=("post script")

不过,在这里,如果您想一次性查看全部文本内容,则必须记住要“展平”数组

echo "$text"      # only outputs index [0], the first line
echo "${text[*]}" # output complete text (joined by first character of IFS)

(整数索引数组是隐式排序的,这与关联数组不同),这使您稍有灵活性,因为您可以根据需要操纵线,甚至可以切片和切块

最后,使用readreadarray和“此处文档”:

read -r -d '' text <<-"EOT"
        Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod
        tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, 
        quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea ...
EOT

readarray -t textarray <<-"EOT"
        Lorem [...]
EOT  

此处文档的形式<<-表示从输入中删除了所有前导硬标签,因此您必须使用标签来缩进文本。用引号引起来"EOT"可防止shell扩展功能,因此逐字使用输入。有了read它,它使用NUL字节分隔输入,因此它将一口气读取换行符分隔的文本。使用readarray(aka mapfile,自bash-4.0起可用),它读入一个数组,并-t在每行上去除换行符。


1
read选项here document非常好!有助于嵌入python脚本并使用执行python -c。我script=$(cat <here document>)以前曾经做过,但是read -r -d '' script <here document>要好得多。
haridsv

9

有一种特殊的heredoc语法,它删除所有行开头的制表符:“ <<-”(请注意添加了破折号)

http://tldp.org/LDP/abs/html/here-docs.html

示例19-4 多行消息,带有制表符

您可以像这样使用它:

v="$(cat <<-EOF
    A
        B
    C
EOF
)"
echo "$v"

结果:

A
B
C

它仅适用于制表符,不适用于空格。


在ubuntu上,情况正好相反-空格起作用,而不是制表符。\t如果您需要标签,效果很好。只是使用echo -e "$v"而不是echo "$v"启用反斜杠字符
will-ob 2015年

5

让外壳吃掉不需要的换行符和以下空格:

$ cat weird.sh 
#!/bin/sh

        var1="A weird(?) $(
             )multi line $(
             )text idea. $(
             )PID=$$"

        var2='You can '$(
            )'avoid expansion '$(
            )'too: PID=$$'

        var3='Or mix it: '$(
            )'To insert the PID use $$. '$(
            )"It expands to e.g. $$."

        echo "$var1"
        echo "$var2"
        echo "$var3"
$ sh weird.sh 
A weird(?) multi line text idea. PID=13960
You can avoid expansion too: PID=$$
Or mix it: To insert the PID use $$. It expands to e.g. 13960.

所以有可能...但是确定喜欢或不喜欢此解决方案只是一个品味问题...


3
其中的每一个都是叉子。
mikeserv

5

也许您可以尝试一下。

          echo "Test" \
               "Test2" \
               "Test3"

5

我建议您应该这样做,然后解释原因,但是首先我想谈谈其他事情...

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"

这里提供的许多其他解决方案似乎都建议您可以通过更改扩展变量的方式以某种方式影响shell变量的内容。我可以向您保证情况并非如此。

    string="some stuff here \
            some more stuff here."
    echo $string ${#string} 
    echo "$string" "${#string}"

输出值

some stuff here some more stuff here. 53
some stuff here                 some more stuff here. 53

上面您看到的是首先是一个字段拆分的扩展,然后是扩展源变量的字节数报告,然后是用引号分隔的扩展和相同的字节数。尽管输出可能有所不同,$string但除了赋值外,shell变量的内容根本不会改变。

更重要的是,如果您不明白为什么会这样,那么您一定会早于此遇到一些令人讨厌的惊喜。让我们再试一次,但条件略有不同。

    IFS=sf
    echo $string ${#string} 
    echo "$string" "${#string}"

相同$string-不同的环境。

输出值

 ome  tu   here                  ome more  tu   here. 53
some stuff here                 some more stuff here. 53

字段拆分基于中定义的字段分隔符进行$IFS。有两种分隔符- $IFS空格和$IFS其他任何分隔符。默认情况下$IFS,将为其分配值空间选项卡换行符 -这是三个可能的$IFS空格值。但是,您可以轻松地对其进行更改,如您在上面看到的那样,并且可能会对现场拆分扩展产生巨大影响。

$IFS空格将按顺序移至单个字段-这就是为什么echo$IFS包含空格的情况下扩展包含任何空格序列的扩展将仅求值单个空格的原因-因为echo将其参数连接到空格。但是,任何非空白值都不会以相同的方式消失,并且每个出现的定界符都会始终获得一个自身的字段-如上述填充扩展所示。

这不是最坏的情况。考虑这其他$string

IFS=$space$tab$newline
cd emptydir
    string=" * * * \
             * * * "
    echo $string ${#string}
    echo "$string" "${#string}"    

输出值

* * * * * * 30
 * * *                  * * *  30

看起来还好吧?好吧,让我们再次改变环境。

    touch file1 file2 file3 file4 file5
    echo $string ${#string}
    echo "$string" "${#string}"    

输出值

file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 file1 file2 file3 file4 file5 30
 * * *                  * * *  30

哇。

默认情况下,如果可以匹配,shell将扩展文件名glob。这发生参数扩展和按其解析顺序进行字段拆分之后,因此任何未引用的字符串都容易受到这种攻击。您可以根据需要使用来关闭此行为set -f,但是默认情况下,任何与POSIX兼容的shell始终会显示glob。

这是您在扩展中添加引号以适合缩进首选项时遇到的问题。即使如此,在任何情况下,无论其扩展行为如何,的实际值$string始终始终是您上一次为其分配时的实际值。让我们回到第一件事。

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"
echo "$var" "${#var}"

输出值

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like. 70

我相信这是一种使shell语法适应缩进首选项的更明智的方法。我在上面所做的是将每个单独的字符串分配给一个位置参数-每个位置参数都可以由数字(如$1or)引用${33},然后将其连接值分配给$var使用特殊的shell参数$*

$IFS即使这样,这种方法也不能幸免。不过,我认为这与$IFS这方面的额外利益有关。考虑:

IFS=\ ;space_split="$*"
IFS=/; slash_split="$*";IFS='
';new_line_split="$*"

echo "$space_split"
echo "$slash_split"
echo "$new_line_split"

输出值

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like.
Arg 1: Line 1./Arg 2: Line 2./and so on for/as long as you might like.
Arg 1: Line 1.
Arg 2: Line 2.
and so on for
as long as you might like.

如您所见,$*将每个arg串联在"$@"中的第一个字节上$IFS。因此,在$IFS为每个保存的值分配不同的值时保存它的值会得到不同的字段分隔符。顺便说一下,您在上面看到的是每个变量的文字值。如果您根本不需要分隔符,则可以执行以下操作:

IFS=;delimitless="$*"
echo "$delimitless" "${#delimitless}"

输出值

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67


2

使用Bash替代扩展

如果您使用的是Bash,则可以使用替换扩展。例如:

$ echo "${VAR//  /}"
This displays with extra spaces.

1

这是用于设置类似路径的变量的变体:

set -- "${MYDIR}/usr/local/lib" \
      :"${MYDIR}/usr/lib" \
      :"${MYDIR}/lib" \
       "${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
    export LD_LIBRARY_PATH="$*"
    LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)

使用setoverwrite $@,可以将其保存并在以后使用,如下所示:

ARGV=("$@")
exec foo "${ARGV[@]}"

LD_LIBRARY_PATH=$(sed 's/ :/:/g' <<< $LD_LIBRARY_PATH)行消除了冒号前的空格以及可能的尾随空格。如果仅消除尾随空格,请LD_LIBRARY_PATH=${LD_LIBRARY_PATH%% }改用。

整个方法是mikeserv出色答案的一种变体。


0

不要害怕空格字符。在打印多行文本之前,只需删除它们。

$ cat ./t.sh
#!/bin/bash

NEED_HELP=1
if [[ $NEED_HELP -eq 1 ]]; then

    lucky_number=$((1 + RANDOM % 10 + 10))

    read -r -d '' text <<'    EOF'
    NAME says hello

    Look at this helpful text:

                                                 * -**
                                 Eyjafjallajokull        Eyjafjallajokull
                            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

    Don't go away, please rate your experience... It'll just take two minutes.

    EOF
    text=$(echo "$text" | sed -r 's!^\s{4}!!')
    text=$(echo "$text" | sed -r "s!\bNAME\b!$0!") # Bash: text=${text//NAME/$0}
    text=$(echo "$text" | sed -r "s!\btwo\b!$lucky_number!")

    echo "$text"

fi

输出:

$ ./t.sh
./t.sh says hello

Look at this helpful text:

                                             * -**
                             Eyjafjallajokull        Eyjafjallajokull
                        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
                Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
            Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
        Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
    Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull
Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull Eyjafjallajokull

Don't go away, please rate your experience... It'll just take 16 minutes.

无需使用<<-Heredoc并用制表符将4位空格缩进。

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.