I have this multi-line string (quotes included):
我有这个多行字符串(包括引号):
abc'asdf"
$(dont-execute-this)
foo"bar"''
How would I assign it to a variable using a heredoc in Bash?
在Bash中,我如何将它分配给一个变量?
I need to preserve newlines.
我需要保存换行符。
I don't want to escape the characters in the string, that would be annoying...
我不想转义字符串中的字符,那会很烦人……
11 个解决方案
#1
372
You can avoid a useless use of cat
and handle mismatched quotes better with this:
您可以避免不必要的使用cat,更好地处理不匹配的引号:
$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
If you don't quote the variable when you echo it, newlines are lost. Quoting it preserves them:
如果在回显时不引用变量,就会丢失新行。引用它保留:
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''
If you want to use indentation for readability in the source code, use a dash after the less-thans. The indentation must be done using only tabs (no spaces).
如果您想在源代码中使用缩进来提高可读性,请在小于thans之后使用短横线。缩进必须只使用制表符(没有空格)。
$ read -r -d '' VAR <<-'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''
If, instead, you want to preserve the tabs in the contents of the resulting variable, you need to remove tab from IFS
. The terminal marker for the here doc (EOF
) must not be indented.
如果您希望在结果变量的内容中保留选项卡,则需要从IFS中删除选项卡。此处doc (EOF)的终端标记不能缩进。
$ IFS='' read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''
Tabs can be inserted at the command line by pressing Ctrl-V Tab. If you are using an editor, depending on which one, that may also work or you may have to turn off the feature that automatically converts tabs to spaces.
选项卡可以通过按Ctrl-V选项卡插入命令行。如果您正在使用一个编辑器(取决于哪个编辑器),那么它也可以工作,或者您可能需要关闭自动将制表符转换为空格的特性。
#2
121
Use $() to assign the output of cat
to your variable like this:
使用$()将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
Making sure to delimit starting END_HEREDOC with single-quotes.
确保用单引号分隔开始的END_HEREDOC。
Note that ending heredoc delimiter END_HEREDOC
must be alone on the line (hence ending parenthesis is on the next line).
请注意,在这条线上,必须单独使用结束本文档分隔符END_HEREDOC(因此结束括号在下一行)。
Thanks to @ephemient
for the answer.
感谢@ephemient的回复。
#3
70
this is variation of Dennis method, looks more elegant in the scripts.
这是Dennis方法的变体,在脚本中看起来更优雅。
function definition:
函数定义:
define(){ IFS='\n' read -r -d '' ${1} || true; }
usage:
用法:
define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
echo "$VAR"
enjoy
享受
p.s. made a 'read loop' version for shells that do not support read -d
. should work with set -eu
and unpaired backticks, but not tested very well:
p.s.为不支持read -d的shell制作了一个“读循环”版本。应该使用set -eu和未配对的背勾,但测试不是很好:
define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
#4
25
VAR=<<END
abc
END
doesn't work because you are redirecting stdin to something that doesn't care about it, namely the assignment
不工作是因为你将stdin重定向到不关心它的东西,也就是作业
export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A
works, but there's a back-tic in there that may stop you from using this. Also, you should really avoid using backticks, it's better to use the command substitution notation $(..)
.
这是可行的,但是里面有个背景,可能会阻止你使用它。此外,您应该避免使用回勾,最好使用命令替换符号$(.. .)。
export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
#5
22
Adding comment here as an answer since I don't have enough rep points to comment on your question text.
在这里添加注释作为答案,因为我没有足够的代表点来评论您的问题文本。
There is still no solution that preserves newlines.
仍然没有保留换行的解决方案。
This is not true - you're probably just being misled by the behaviour of echo:
这不是真的——你可能只是被echo的行为误导了:
echo $VAR # strips newlines
echo $VAR #删除换行
echo "$VAR" # preserves newlines
echo“$VAR”#保存新行
#6
6
An array is a variable, so in that case mapfile will work
数组是一个变量,所以在这种情况下,mapfile会工作
mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z
Then you can print like this
然后你可以像这样打印
printf %s "${y[@]}"
#7
5
Branching off Neil's answer, you often don't need a var at all, you can use a function in much the same way as a variable and it's much easier to read than the inline or read
-based solutions.
根据Neil的答案,您通常不需要任何var,您可以像使用变量一样使用函数,并且它比内联或基于读的解决方案更容易读取。
$ 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"''
#8
4
assign a heredoc value to a variable
VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"
used as an argument of a command
echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx 123123 123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"
#9
1
I found myself having to read a string with NULL in it, so here is a solution that will read anything you throw at it. Although if you actually are dealing with NULL, you will need to deal with that at the hex level.
我发现我必须读取一个带空值的字符串,所以这里有一个解可以读取你向它抛出的任何东西。尽管如果您实际处理的是NULL,那么您将需要在十六进制级别处理它。
$ cat > read.dd.sh
$猫> 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 )
}
Proof:
证明:
$ . 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"
$
HEREDOC example (with ^J, ^M, ^I):
HEREDOC示例(J ^,^,^我):
$ 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
#10
0
Thanks to dimo414's answer, this shows how his great solution works, and shows that you can have quotes and variables in the text easily as well:
多亏了dimo414的回答,这显示了他伟大的解决方案是如何工作的,也显示了你可以在文本中轻松地使用引号和变量:
example output
$ ./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
#11
-7
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT
#1
372
You can avoid a useless use of cat
and handle mismatched quotes better with this:
您可以避免不必要的使用cat,更好地处理不匹配的引号:
$ read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
If you don't quote the variable when you echo it, newlines are lost. Quoting it preserves them:
如果在回显时不引用变量,就会丢失新行。引用它保留:
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''
If you want to use indentation for readability in the source code, use a dash after the less-thans. The indentation must be done using only tabs (no spaces).
如果您想在源代码中使用缩进来提高可读性,请在小于thans之后使用短横线。缩进必须只使用制表符(没有空格)。
$ read -r -d '' VAR <<-'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''
If, instead, you want to preserve the tabs in the contents of the resulting variable, you need to remove tab from IFS
. The terminal marker for the here doc (EOF
) must not be indented.
如果您希望在结果变量的内容中保留选项卡,则需要从IFS中删除选项卡。此处doc (EOF)的终端标记不能缩进。
$ IFS='' read -r -d '' VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
$ echo "$VAR"
abc'asdf"
$(dont-execute-this)
foo"bar"''
Tabs can be inserted at the command line by pressing Ctrl-V Tab. If you are using an editor, depending on which one, that may also work or you may have to turn off the feature that automatically converts tabs to spaces.
选项卡可以通过按Ctrl-V选项卡插入命令行。如果您正在使用一个编辑器(取决于哪个编辑器),那么它也可以工作,或者您可能需要关闭自动将制表符转换为空格的特性。
#2
121
Use $() to assign the output of cat
to your variable like this:
使用$()将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
Making sure to delimit starting END_HEREDOC with single-quotes.
确保用单引号分隔开始的END_HEREDOC。
Note that ending heredoc delimiter END_HEREDOC
must be alone on the line (hence ending parenthesis is on the next line).
请注意,在这条线上,必须单独使用结束本文档分隔符END_HEREDOC(因此结束括号在下一行)。
Thanks to @ephemient
for the answer.
感谢@ephemient的回复。
#3
70
this is variation of Dennis method, looks more elegant in the scripts.
这是Dennis方法的变体,在脚本中看起来更优雅。
function definition:
函数定义:
define(){ IFS='\n' read -r -d '' ${1} || true; }
usage:
用法:
define VAR <<'EOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
EOF
echo "$VAR"
enjoy
享受
p.s. made a 'read loop' version for shells that do not support read -d
. should work with set -eu
and unpaired backticks, but not tested very well:
p.s.为不支持read -d的shell制作了一个“读循环”版本。应该使用set -eu和未配对的背勾,但测试不是很好:
define(){ o=; while IFS="\n" read -r a; do o="$o$a"'
'; done; eval "$1=\$o"; }
#4
25
VAR=<<END
abc
END
doesn't work because you are redirecting stdin to something that doesn't care about it, namely the assignment
不工作是因为你将stdin重定向到不关心它的东西,也就是作业
export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A
works, but there's a back-tic in there that may stop you from using this. Also, you should really avoid using backticks, it's better to use the command substitution notation $(..)
.
这是可行的,但是里面有个背景,可能会阻止你使用它。此外,您应该避免使用回勾,最好使用命令替换符号$(.. .)。
export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
#5
22
Adding comment here as an answer since I don't have enough rep points to comment on your question text.
在这里添加注释作为答案,因为我没有足够的代表点来评论您的问题文本。
There is still no solution that preserves newlines.
仍然没有保留换行的解决方案。
This is not true - you're probably just being misled by the behaviour of echo:
这不是真的——你可能只是被echo的行为误导了:
echo $VAR # strips newlines
echo $VAR #删除换行
echo "$VAR" # preserves newlines
echo“$VAR”#保存新行
#6
6
An array is a variable, so in that case mapfile will work
数组是一个变量,所以在这种情况下,mapfile会工作
mapfile y <<z
abc'asdf"
$(dont-execute-this)
foo"bar"''
z
Then you can print like this
然后你可以像这样打印
printf %s "${y[@]}"
#7
5
Branching off Neil's answer, you often don't need a var at all, you can use a function in much the same way as a variable and it's much easier to read than the inline or read
-based solutions.
根据Neil的答案,您通常不需要任何var,您可以像使用变量一样使用函数,并且它比内联或基于读的解决方案更容易读取。
$ 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"''
#8
4
assign a heredoc value to a variable
VAR="$(cat <<'VAREOF'
abc'asdf"
$(dont-execute-this)
foo"bar"''
VAREOF
)"
used as an argument of a command
echo "$(cat <<'SQLEOF'
xxx''xxx'xxx'xx 123123 123123
abc'asdf"
$(dont-execute-this)
foo"bar"''
SQLEOF
)"
#9
1
I found myself having to read a string with NULL in it, so here is a solution that will read anything you throw at it. Although if you actually are dealing with NULL, you will need to deal with that at the hex level.
我发现我必须读取一个带空值的字符串,所以这里有一个解可以读取你向它抛出的任何东西。尽管如果您实际处理的是NULL,那么您将需要在十六进制级别处理它。
$ cat > read.dd.sh
$猫> 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 )
}
Proof:
证明:
$ . 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"
$
HEREDOC example (with ^J, ^M, ^I):
HEREDOC示例(J ^,^,^我):
$ 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
#10
0
Thanks to dimo414's answer, this shows how his great solution works, and shows that you can have quotes and variables in the text easily as well:
多亏了dimo414的回答,这显示了他伟大的解决方案是如何工作的,也显示了你可以在文本中轻松地使用引号和变量:
example output
$ ./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
#11
-7
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT