缩进时如何将字符串值分配给多行的变量?

Jam*_*esL 75 variable

问题:

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

所以,我试图使用多条线来分配它。

没有缩进很简单:

VAR="This displays without \
any issues."
echo "${VAR}"
Run Code Online (Sandbox Code Playgroud)

结果:

This displays without any issues.
Run Code Online (Sandbox Code Playgroud)

但是有缩进:

    VAR="This displays with \
    extra spaces."
    echo "${VAR}"
Run Code Online (Sandbox Code Playgroud)

结果:

This displays with      extra spaces.
Run Code Online (Sandbox Code Playgroud)

如果没有这些空格,我如何优雅地分配它?

Kan*_*han 45

这里的问题是你用双引号 ("") 包围了变量。删除它,一切都会好起来的。

    VAR="This displays with \
    extra spaces."
    echo ${VAR}
Run Code Online (Sandbox Code Playgroud)

输出

 This displays with extra spaces.
Run Code Online (Sandbox Code Playgroud)

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

例如,

$ echo "Hello     World    ........ ...            ...."
Run Code Online (Sandbox Code Playgroud)

将打印

Hello     World    ........ ...            ....
Run Code Online (Sandbox Code Playgroud)

在删除引号时,它的不同

$ echo Hello     World    ........ ...            ....
Hello World ........ ... ....
Run Code Online (Sandbox Code Playgroud)

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

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

在下面的命令中,echo只获取单个参数"Hello World"

$ variable="Hello World"
$ echo "$variable"
Run Code Online (Sandbox Code Playgroud)

但是在下面的情况下,echo会得到两个参数作为Hello和World

$ variable="Hello World"
$ echo $variable
Run Code Online (Sandbox Code Playgroud)

  • @Sman865 - 当我告诉你这实际上几乎肯定是这里提供的最复杂的答案时,请相信我,而且在大多数方面也是错误的 - 特别是在它的开场声明中。任何围绕赋值的问题都不能与它后来的扩展有任何关系——这只是倒退。我很抱歉 Kannan,但这个答案既错误又错误。`$IFS` 上的字段拆分扩展是一个强大且通用的工具 - 但以这种方式编写的代码永远不会产生任何类型的可靠结果。 (20认同)
  • 扩展变量时不使用引号迟早会导致问题,从文件名扩展(任何`*``?``[]`)开始。 (3认同)
  • 我同意@mikeserv,如果从表面上看,这是一个非常糟糕的建议。如果使用而不产生任何后果,这将被破坏。例如,如果变量作为参数传递给命令,您不希望每个单词被分解为单独的参数。 (2认同)

mr.*_*tic 35

esuoxu和Mickaël Bucas给出的解决方案是执行此操作的常见且更便携的方法。

这里有一些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 ..." 
Run Code Online (Sandbox Code Playgroud)

如果您想在文本中使用换行符(或其他空格/转义符),请改用$''引用:

text=$'Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod\n'
text+=$'...'
Run Code Online (Sandbox Code Playgroud)

接下来,使用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 ..."
Run Code Online (Sandbox Code Playgroud)

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

接下来,使用数组:

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 ..." )
Run Code Online (Sandbox Code Playgroud)

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

text+=("post script")
Run Code Online (Sandbox Code Playgroud)

不过,在这里,如果您想要一次性获得整个文本内容,您必须记住“展平”数组

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

(整数索引数组是隐式排序的,与关联数组不同)这为您提供了更大的灵活性,因为您可以根据需要操作线条甚至切片和切块。

最后,使用readorreadarray和“here-document”:

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  
Run Code Online (Sandbox Code Playgroud)

here-document 形式<<-意味着从输入中删除所有前导硬制表符,因此您必须使用制表符来缩进文本。引号周围"EOT"防止 shell 扩展功能,因此输入是逐字使用的。有了read它使用NULL字节分隔的输入,因此,将在一气呵成读换行符分隔文本。使用readarray(aka mapfile,自 bash-4.0 起可用),它读入一个数组,并-t在每一行上去除换行符。


小智 15

有一个特殊的 heredoc 语法可以删除所有行开头的制表符:“<<-”(注意添加的破折号)

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

例 19-4。多行消息,制表符被抑制

你可以这样使用它:

v="$(cat <<-EOF
    A
        B
    C
EOF
)"
echo "$v"
Run Code Online (Sandbox Code Playgroud)

结果 :

A
B
C
Run Code Online (Sandbox Code Playgroud)

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


mik*_*erv 6

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

set -- 'Arg 1: Line 1.' \
       'Arg 2: Line 2.' \
       'and so on for'  \
       'as long as you might like.'
var="$*"
Run Code Online (Sandbox Code Playgroud)

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

    string="some stuff here \
            some more stuff here."
    echo $string ${#string} 
    echo "$string" "${#string}"
Run Code Online (Sandbox Code Playgroud)

输出

some stuff here some more stuff here. 53
some stuff here                 some more stuff here. 53
Run Code Online (Sandbox Code Playgroud)

您在上面看到的首先是字段拆分扩展,然后是扩展源变量的字节数报告,然后是引号分隔的扩展,以及相同的字节数。虽然输出可能不同,$string但除了赋值之外,shell 变量的内容从不改变。

更重要的是,如果你不明白这是为什么,你迟早会遇到一些非常讨厌的惊喜。让我们再试一次,但条件略有不同。

    IFS=sf
    echo $string ${#string} 
    echo "$string" "${#string}"
Run Code Online (Sandbox Code Playgroud)

相同$string- 不同的环境。

输出

 ome  tu   here                  ome more  tu   here. 53
some stuff here                 some more stuff here. 53
Run Code Online (Sandbox Code Playgroud)

字段拆分基于 中定义的字段分隔符$IFS。有两种分隔符 -$IFS空格和$IFS其他任何东西。默认情况下$IFS,分配了值空间制表符换行符- 这是三个可能的$IFS空白值。但是,正如您在上面看到的那样,它很容易更改,并且可以对场分裂扩展产生巨大影响。

$IFS空格将按顺序省略到单个字段 - 这就是为什么echo当$IFS包含空格时包含任何空格序列的扩展将评估为仅一个空格 - 因为echo将其参数连接在空格上。但是任何非空白值都不会以相同的方式消失,并且每个出现的分隔符总是有一个字段 - 正如上面的内容扩展中所见。

这还不是最糟糕的。考虑这个其他$string。

IFS=$space$tab$newline
cd emptydir
    string=" * * * \
             * * * "
    echo $string ${#string}
    echo "$string" "${#string}"    
Run Code Online (Sandbox Code Playgroud)

输出

* * * * * * 30
 * * *                  * * *  30
Run Code Online (Sandbox Code Playgroud)

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

    touch file1 file2 file3 file4 file5
    echo $string ${#string}
    echo "$string" "${#string}"    
Run Code Online (Sandbox Code Playgroud)

输出

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
Run Code Online (Sandbox Code Playgroud)

哇。

默认情况下,shell 将扩展文件名 globs,如果它可以匹配它们。这发生在参数扩展和按解析顺序拆分字段之后,因此任何未加引号的字符串都容易受到这种方式的攻击。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}"
Run Code Online (Sandbox Code Playgroud)

输出

Arg 1: Line 1. Arg 2: Line 2. and so on for as long as you might like. 70
Run Code Online (Sandbox Code Playgroud)

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

$IFS即使如此,这种方法也不能幸免。尽管如此,我认为它与$IFS这方面的额外好处的关系。考虑:

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

echo "$space_split"
echo "$slash_split"
echo "$new_line_split"
Run Code Online (Sandbox Code Playgroud)

输出

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.
Run Code Online (Sandbox Code Playgroud)

如您所见,$*将每个 arg in 连接"$@"到$IFS. 因此,$IFS在不同分配时保存其值会为每个保存的值获得不同的字段分隔符。顺便说一下,你在上面看到的是每个变量的文字值。如果您根本不需要分隔符,您可以这样做:

IFS=;delimitless="$*"
echo "$delimitless" "${#delimitless}"
Run Code Online (Sandbox Code Playgroud)

输出

Arg 1: Line 1.Arg 2: Line 2.and so on foras long as you might like. 67
Run Code Online (Sandbox Code Playgroud)


小智 5

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

$ 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.
Run Code Online (Sandbox Code Playgroud)

所以这是可能的......但是肯定喜欢或不喜欢这个解决方案是一个品味问题......

  • 每一个都是一个叉子。 (3认同)