我有一个包含引号的多行字符串:
abc'asdf"
$(dont-execute-this)
foo"bar"''
在Bash中,我如何使用heredoc将其分配给变量?
我需要保留换行符。
我不想转义字符串中的字符,那样很麻烦...
您可以通过以下方式避免无用的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
中删除制表符。Here文档的终端标记(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 来插入制表符。如果您正在使用编辑器,根据不同的编辑器,这也可能有效,或者您可能需要关闭自动将制表符转换为空格的功能。
set -o errexit
(也称为 set -e
),并且当您使用 read
命令时,它会在读取到 EOF 时返回一个非零返回码,从而终止您的脚本。请注意不要修改原意,尽可能地使翻译通俗易懂。 - Mark Byersset -e
并且总是建议不要使用它的原因之一。最好使用适当的错误处理机制,而不是使用set -e
。 trap
是你的朋友,还有其他朋友,比如 else
和 ||
。 - Dennis Williamsoncat
值得吗?使用 cat
将一个 heredoc 赋值给一个变量是一个众所周知的习语。在我看来,使用 read
可能会使事情变得更加难以理解,而收益微不足道。 - Gregory Pakoszd
和空字符串之间没有空格;在bash
将-rd''
缩写为-rd
之前,read
从未看到其参数,因此VAR
被视为-d
的参数。 - chepnerread
会返回一个非零的退出代码。这使得在启用错误检查的脚本(例如set -e
)中使用此方法并不理想。 - Swiss使用 $() 将 cat
命令的输出赋值给变量,如下所示:
VAR=$(
cat <<'END_HEREDOC'
abc'asdf"
$(dont-execute-this)
foo"bar"''
END_HEREDOC
)
# this will echo variable with new lines intact
echo "$VAR"
# this will echo variable without new lines (changed to space character)
echo $VAR
确保用单引号来定界起始的 END_HEREDOC。 这将防止 heredoc 的内容被扩展,因此 dont-execute-this
将不会被执行。
请注意,结束 heredoc 定界符 END_HEREDOC
必须单独占据一行(因此结束括号 )
在下一行)。
感谢 @ephemient
的回答。
echo "$VAR"
而不是 echo $VAR
。 - sevkoash
和OpenWRT时,由于read
不支持-d
选项,这个方法非常有用。” - David Ehrmann这是Dennis方法的一种变体,在脚本中看起来更加优雅。
函数定义:
define(){ IFS='\n' read -r -d '' ${1} || true; }
使用方法:
define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
echo "$VAR"
享受
附言:为不支持 read -d
的Shell制作了一个'read loop'版本。应该能够与 set -eu
和非成对的反引号一起使用,但测试还不够充分:
define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
set -e
,而所选答案则不适用。这似乎是因为 http://unix.stackexchange.com/a/265151/20650
。 - ffledglingdefine(){ IFS=$'\n' ...
(添加了 $
)。 - luckman212IFS='\n'
并不是你想要的(它将 IFS 设置为一个字面上的 n
,并删除反斜杠)。你可以通过在引号之间添加一个字面上的换行符来解决这个问题,或者在 Bash 中使用先前评论中建议的 IFS=$'\n'
。 - tripleeeVAR=<<END
abc
END
不工作是因为你正在将stdin重定向到一个不关心它的地方,即该赋值语句。
export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A
这段代码可以运行,但其中有一个反引号可能会导致问题。此外,你应该避免使用反引号,最好使用命令替换符号$(..)
。
export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
$(cat <<'END'
)替代即可接近成功。最后一个换行符将不会成为变量的一部分,但其余部分将被保留。 - ephemientexport A=$(<<END ... END)
同样可以正常工作,并且少了一个进程运行。 - SpoikeREM=<< 'REM' ... 在此处添加注释... REM
。或者更简洁地说,: << 'REM' ...
。其中“REM”可以是像“NOTES”或“SCRATCHPAD”等内容。 - Beejor仍然没有保留换行符的解决方案。
这并不是真的-你可能会被echo的行为所误导:
echo $VAR # 去除换行符
echo "$VAR" # 保留换行符
在Neil的回答的基础上,你通常根本不需要一个var,你可以像使用变量一样使用函数,并且它比内联或基于read
的解决方案更容易阅读。
$ complex_message() {
cat <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
}
$ echo "This is a $(complex_message)"
This is a abc'asdf"
$(dont-execute-this)
foo"bar"''
mapfile y <<'z'
abc'asdf"
$(dont-execute-this)
foo"bar"''
z
然后你可以像这样打印
printf %s "${y[@]}"
我无法相信我是第一个发布这个的人。
@Erman和@Zombo接近了,但是mapfile
不仅仅读取数组...
考虑这个例子:
#!/bin/bash
mapfile -d '' EXAMPLE << 'EOF'
Hello
こんにちは
今晩は
小夜なら
EOF
echo -n "$EXAMPLE"
Yielding:
你好 こんにちは 今晩は 如果是夜深了
$''
是给 mapfile
的分隔符,它永远不会出现,它的含义是“未被分隔”。
因此,没有必要使用无用的 cat
并且不需要重组数组而产生额外开销。
此外,您将获得以下好处:
$ echo $EXAMPLE
Hello こんにちは 今晩は 小夜なら
使用 @Zombo 的方法无法获得以下内容:
mapfile y <<'z'
abc'asdf"
$(dont-execute-this)
foo"bar"''
z
echo $y
如果通过head -c -1
命令运行,也可以以不影响性能的方式摆脱最后一个换行符:
abc'asdf"
如果你通过 head -c -1
命令运行,还可以以不影响性能的方式摆脱最后一个换行符:
unset EXAMPLE
mapfile -d '' EXAMPLE < <(head -c -1 << EOF
Hello
こんにちは
今晩は
小夜なら
EOF
)
printf "%q" "$EXAMPLE"
$'Hello\nこんにちは\n今晩は\n小夜なら'
mapfile -d
需要 Bash 4.4 或更高版本。 - MartinVAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"
echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx 123123 123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"
echo "$VAR"
。 - Brad Parks$ ./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
#!/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
别惊慌,有“$COUNT”个文件
,这样撇号/单引号会让事情变得有趣。 - dragon788
'EOF'
和在内容中使用转义换行符\
的 heredoc 赋值:如果第二行有cd
命令,我会得到 ".sh: line X: cd: command not found";但是如果我双引号"EOF"
;那么 bash 变量${A}
不会被保留为字符串(它们会被扩展);但是,换行符 会 被保留 - 并且,我没有问题在第二行运行带有cd
的命令(_并且 'EOF' 和 "EOF" 似乎也与eval
配合良好,用于运行存储在字符串变量中的一组命令_)。干杯! - sdaaueval $VAR
,bash注释“#”将导致其余脚本被注释掉,因为此处$VAR将被视为单行;为了能够在多行脚本中使用bash“#”注释,请在eval
调用中也对变量进行双引号包裹:eval "$VAR"
。 - sdaaueval
方法时遇到了问题,但由于它是某个包中的一部分,该包会对其配置文件中定义的某些变量进行eval
操作,因此我没有追踪到问题所在。错误信息为:“/usr/lib/network/network: eval: line 153: syntax error: unexpected end of file”。我只是转而使用了另一种解决方案。 - Golar Ramblar