我有这个多行字符串(包括引号):

abc'asdf"
$(dont-execute-this)
foo"bar"''

我将如何分配它到一个变量使用heredoc在Bash?

我需要保留换行符。

我不想转义字符串中的字符,这将是恼人的…


当前回答

你可以避免无用的cat使用,并更好地处理不匹配的引号:

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

如果回显时不引用变量,则会丢失换行符。引用它来保存它们:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

如果您想在源代码中使用缩进来提高可读性,请在小于号之后使用破折号。缩进必须只使用制表符(没有空格)。

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

相反,如果希望保留结果变量内容中的制表符,则需要从IFS中删除制表符。这里文档(EOF)的终端标记不能缩进。

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

通过按Ctrl-V Tab可以在命令行中插入选项卡。如果您正在使用编辑器,这取决于哪种编辑器,也可以工作,或者您可能必须关闭自动将制表符转换为空格的功能。

其他回答

仍然没有保留换行符的解决方案。

这不是真的——你可能只是被echo的行为误导了:

返回$VAR #换行符

echo "$VAR" #保留换行符

你可以避免无用的cat使用,并更好地处理不匹配的引号:

$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF

如果回显时不引用变量,则会丢失换行符。引用它来保存它们:

$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

如果您想在源代码中使用缩进来提高可读性,请在小于号之后使用破折号。缩进必须只使用制表符(没有空格)。

$ read -r -d '' VAR <<-'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
    EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''

相反,如果希望保留结果变量内容中的制表符,则需要从IFS中删除制表符。这里文档(EOF)的终端标记不能缩进。

$ IFS='' read -r -d '' VAR <<'EOF'
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''
EOF
$ echo "$VAR"
    abc'asdf"
    $(dont-execute-this)
    foo"bar"''

通过按Ctrl-V Tab可以在命令行中插入选项卡。如果您正在使用编辑器,这取决于哪种编辑器,也可以工作,或者您可能必须关闭自动将制表符转换为空格的功能。

感谢dimo414的回答,这展示了他的伟大解决方案是如何工作的,并表明你可以在文本中轻松地使用引号和变量:

示例输出

$ ./test.sh

The text from the example function is:
  Welcome dev: Would you "like" to know how many 'files' there are in /tmp?

  There are "      38" files in /tmp, according to the "wc" command

test.sh

#!/bin/bash

function text1()
{
  COUNT=$(\ls /tmp | wc -l)
cat <<EOF

  $1 Would you "like" to know how many 'files' there are in /tmp?

  There are "$COUNT" files in /tmp, according to the "wc" command

EOF
}

function main()
{
  OUT=$(text1 "Welcome dev:")
  echo "The text from the example function is: $OUT"
}

main
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT

我发现自己必须读取一个NULL字符串,所以这里有一个解决方案,将读取任何你扔在它。虽然如果你实际上是在处理NULL,你将需要在十六进制级别上处理它。

$ cat > read.dd.sh

read.dd() {
     buf= 
     while read; do
        buf+=$REPLY
     done < <( dd bs=1 2>/dev/null | xxd -p )

     printf -v REPLY '%b' $( sed 's/../ \\\x&/g' <<< $buf )
}

证明:

$ . read.dd.sh
$ read.dd < read.dd.sh
$ echo -n "$REPLY" > read.dd.sh.copy
$ diff read.dd.sh read.dd.sh.copy || echo "File are different"
$ 

这里的例子(有^J, ^M, ^I):

$ read.dd <<'HEREDOC'
>       (TAB)
>       (SPACES)
(^J)^M(^M)
> DONE
>
> HEREDOC

$ declare -p REPLY
declare -- REPLY="  (TAB)
      (SPACES)
(^M)
DONE

"

$ declare -p REPLY | xxd
0000000: 6465 636c 6172 6520 2d2d 2052 4550 4c59  declare -- REPLY
0000010: 3d22 0928 5441 4229 0a20 2020 2020 2028  =".(TAB).      (
0000020: 5350 4143 4553 290a 285e 4a29 0d28 5e4d  SPACES).(^J).(^M
0000030: 290a 444f 4e45 0a0a 220a                 ).DONE