如何在 Bash 中将定界符值赋给变量?

发布于 2024-07-29 01:08:57 字数 194 浏览 5 评论 0原文

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

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

如何在 Bash 中使用定界文档将其分配给变量?

我需要保留换行符。

我不想转义字符串中的字符,那会很烦人......

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?

I need to preserve newlines.

I don't want to escape the characters in the string, that would be annoying...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

爱你不解释 2024-08-05 01:08:58

这是一种(恕我直言)非常优雅并且避免 UUOC 的方法:

  VAR=$(sed -e 's/[ ]*\| //g' -e '1d;$d' <<'--------------------'
      | 
      | <!DOCTYPE html>
      | <html>
      |   <head>
      |     <script src='script.js'></script>
      |   </head>
      |   <body>
      |     <span id='hello-world'></span>
      |   </body>
      | </html>
      | 
--------------------
    )

“|” 字符定义边距,并且在打印字符串中仅考虑边距右侧的空白。 '1d;$d' 删除第一行和最后一行,它们只是作为内容周围的顶部和底部边距添加。 所有内容都可以缩进到您喜欢的任何级别,但 HEREDOC 分隔符除外,在本例中它只是一堆连字符。

echo "$VAR"

# prints

<!DOCTYPE html>
<html>
  <head>
    <script src='script.js'></script>
  </head>
  <body>
    <span id='hello-world'></span>
  </body>
</html>

Here's a way to do it that is (imho) quite elegant and avoids a UUOC:

  VAR=$(sed -e 's/[ ]*\| //g' -e '1d;$d' <<'--------------------'
      | 
      | <!DOCTYPE html>
      | <html>
      |   <head>
      |     <script src='script.js'></script>
      |   </head>
      |   <body>
      |     <span id='hello-world'></span>
      |   </body>
      | </html>
      | 
--------------------
    )

The '|' characters define the margin, and only the whitespace to the right of the margin is respected in the printed string. The '1d;$d' strips the first and last line, which are just added as a top and bottom margin around the content. Everything can be indented to whatever level you like, except the HEREDOC delimiter, which in this case is just a bunch of hyphens.

echo "$VAR"

# prints

<!DOCTYPE html>
<html>
  <head>
    <script src='script.js'></script>
  </head>
  <body>
    <span id='hello-world'></span>
  </body>
</html>
微凉 2024-08-05 01:08:58
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT
$TEST="ok"
read MYTEXT <<EOT
this bash trick
should preserve
newlines $TEST
long live perl
EOT
echo -e $MYTEXT
等风来 2024-08-05 01:08:57

您可以避免无用地使用 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 在命令行插入制表符。 如果您使用的是编辑器(具体取决于哪个编辑器),这也可能有效,或者您可能必须关闭自动将制表符转换为空格的功能。

You can avoid a useless use of cat and handle mismatched quotes better with this:

$ 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).

$ 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='' 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.

空城仅有旧梦在 2024-08-05 01:08:57

使用 $() 将 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将不会被执行。

请注意,结束这里文档分隔符 END_HEREDOC 必须单独出现在该行中(因此结束括号 ) 位于下一行)。

感谢 @ephemient 的回答。

Use $() to assign the output of cat to your variable like this:

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. This will prevent the content of the heredoc from being expanded, so dont-execute-this will not be executed.

Note that ending heredoc delimiter END_HEREDOC must be alone on the line (hence the ending parenthesis ) is on the next line).

Thanks to @ephemient for the answer.

深空失忆 2024-08-05 01:08:57

这是丹尼斯方法的变体,在脚本中看起来更优雅。

函数定义(bash 版本):

define(){ IFS=

用法:

define VAR <<'EOF'
abc 'asdf " \$ $ \n 

更新ksh“读循环”版本(也适用于 bash):

function define { typeset a o=; while IFS= read -r a; do o+="$a"

测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n' read -r -d '' ${1} || true; }

用法:


更新ksh“读循环”版本(也适用于 bash):


测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n' $(dont-exec ute-this) foo " bar " ' ' `bad bad `` EOF echo "$VAR"

更新ksh“读循环”版本(也适用于 bash):


测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n' read -r -d '' ${1} || true; }

用法:

更新ksh“读循环”版本(也适用于 bash):

测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n'; done; o=${o%?}; eval "$1=\$o"; }

测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n' read -r -d '' ${1} || true; }

用法:

更新ksh“读循环”版本(也适用于 bash):

测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n' $(dont-exec ute-this) foo " bar " ' ' `bad bad `` EOF echo "$VAR"

更新ksh“读循环”版本(也适用于 bash):

测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

\n' read -r -d '' ${1} || true; }

用法:

更新ksh“读循环”版本(也适用于 bash):

测试(还包括不删除最后一个换行符)https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

享受

this is variation of Dennis method, looks more elegant in the scripts.

function definition (bash version):

define(){ IFS=

usage:

define VAR <<'EOF'
abc 'asdf " \$ $ \n 

Updated ksh 'read loop' version (also works on bash):

function define { typeset a o=; while IFS= read -r a; do o+="$a"

Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n' read -r -d '' ${1} || true; }

usage:


Updated ksh 'read loop' version (also works on bash):


Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n' $(dont-exec ute-this) foo " bar " ' ' `bad bad `` EOF echo "$VAR"

Updated ksh 'read loop' version (also works on bash):


Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n' read -r -d '' ${1} || true; }

usage:

Updated ksh 'read loop' version (also works on bash):

Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n'; done; o=${o%?}; eval "$1=\$o"; }

Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n' read -r -d '' ${1} || true; }

usage:

Updated ksh 'read loop' version (also works on bash):

Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n' $(dont-exec ute-this) foo " bar " ' ' `bad bad `` EOF echo "$VAR"

Updated ksh 'read loop' version (also works on bash):

Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

\n' read -r -d '' ${1} || true; }

usage:

Updated ksh 'read loop' version (also works on bash):

Tests (also includes versions that does not remove the last newline): https://gist.github.com/ootada/e80b6d7fb86acdcda75d77eb7ade364c

enjoy

鹤仙姿 2024-08-05 01:08:57
VAR=<<END
abc
END

不起作用,因为您将标准输入重定向到不关心它的东西,即作业

export A=`cat <<END
sdfsdf
sdfsdf
sdfsfds
END
` ; echo $A

有效,但其中有一个反引号可能会阻止您使用它。 另外,您确实应该避免使用反引号,最好使用命令替换符号 $(..)

export A=$(cat <<END
sdfsdf
sdfsdf
sdfsfds
END
) ; echo $A
VAR=<<END
abc
END

doesn't work because you are redirecting stdin to something that doesn't care about it, namely the assignment

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
時窥 2024-08-05 01:08:57

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

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

echo $VAR # strips newlines

echo "$VAR" # keeps newlines

There is still no solution that preserves newlines.

This is not true - you're probably just being misled by the behaviour of echo:

echo $VAR # strips newlines

echo "$VAR" # preserves newlines

深巷少女 2024-08-05 01:08:57

分支 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"''

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.

$ 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"''
我一直都在从未离去 2024-08-05 01:08:57

数组是一个变量,所以在这种情况下,mapfile 将起作用

mapfile y <<'z'
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

然后你可以像这样打印

printf %s "${y[@]}"

An array is a variable, so in that case mapfile will work

mapfile y <<'z'
abc'asdf"
$(dont-execute-this)
foo"bar"''
z

Then you can print like this

printf %s "${y[@]}"
屋檐 2024-08-05 01:08:57

我不敢相信我是第一个发布此内容的人。

@Erman 和 @Zombo 很接近,但是 mapfile 不仅仅读取数组...

考虑一下:

#!/bin/bash
mapfile -d '' EXAMPLE << 'EOF'
Hello
こんにちは
今晩は
小夜なら
EOF
echo -n "$EXAMPLE"

Yielding:

Hello
こんにちは
今晩は
小夜なら

$'' 是给 mapfile 的分隔符,它永远不会发生,它的意思是“未分隔”。

因此,不需要无用地使用 cat ,也不需要承受重新组合数组的惩罚。

此外,您还可以获得以下好处:

$ echo $EXAMPLE
Hello こんにちは 今晩は 小夜なら

这是 @Zombo 的方法所没有的:

mapfile y <<'z'
abc'asdf"
$(dont-execute-this)
foo"bar"''
z
echo $y
abc'asdf"

奖励

如果您通过 head -c -1 运行它,您还可以以一种不会的方式摆脱最后一个换行符。不表现不佳:

unset EXAMPLE
mapfile -d '' EXAMPLE < <(head -c -1 << EOF
Hello
こんにちは
今晩は
小夜なら
EOF
)
printf "%q" "$EXAMPLE"

Hello\nこんにちは\n今晩は\n小夜なら'

I can't believe I'm the first to post this.

@Erman and @Zombo are close, but mapfile doesn't just read arrays...

Consider this:

#!/bin/bash
mapfile -d '' EXAMPLE << 'EOF'
Hello
こんにちは
今晩は
小夜なら
EOF
echo -n "$EXAMPLE"

Yielding:

Hello
こんにちは
今晩は
小夜なら

$'' is the delimiter given to mapfile, it will never occur, it means "not delimited".

So there's no need for a useless use of cat and no need to incur the penalty of recombining arrays.

Furthermore, you get this benefit:

$ echo $EXAMPLE
Hello こんにちは 今晩は 小夜なら

Which you do not receive with @Zombo's method:

mapfile y <<'z'
abc'asdf"
$(dont-execute-this)
foo"bar"''
z
echo $y
abc'asdf"

Bonus

If you run it through head -c -1 you can also get rid of that last newline in a way that won't be non-performant:

unset EXAMPLE
mapfile -d '' EXAMPLE < <(head -c -1 << EOF
Hello
こんにちは
今晩は
小夜なら
EOF
)
printf "%q" "$EXAMPLE"

Hello\nこんにちは\n今晩は\n小夜なら'
椵侞 2024-08-05 01:08:57

将heredoc值分配给

VAR="$(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
)"

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
)"
萌无敌 2024-08-05 01:08:57

感谢 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

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:

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
若无相欠,怎会相见 2024-08-05 01:08:57

在许多情况下,此页面的答案过于复杂:

GNUV3="\
    MyProg Copyright (C) $(date +%Y) NVRM 
    This program comes with ABSOLUTELY NO WARRANTY; for details type show w.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type show c for details.
"

echo "$GNUV3"

In many cases, this page has over-complex answers:

GNUV3="\
    MyProg Copyright (C) $(date +%Y) NVRM 
    This program comes with ABSOLUTELY NO WARRANTY; for details type show w.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type show c for details.
"

echo "$GNUV3"
摇划花蜜的午后 2024-08-05 01:08:57

我发现自己必须读取其中包含 NULL 的字符串,因此这里有一个解决方案,可以读取您扔给它的任何内容。 尽管如果您实际上正在处理 NULL,则需要在十六进制级别处理它。

$猫> 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"
$ 

HEREDOC 示例(带有 ^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

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.

$ 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 )
}

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):

$ 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
对你而言 2024-08-05 01:08:57

这将在任何 POSIX shell 中工作,避免 read 的非零退出状态问题,并且看起来比内联 cat 更干净。

def() {
    eval "$1=\$(cat)"
}

def variable << '>>'
abc'asdf"
$(dont-execute-this)
foo"bar"''
>>

警告:这将删除此处文档末尾的换行符。 不在末尾的换行符将被保留。

This will work in any POSIX shell, avoids the issue of the non-zero exit status of read, and looks cleaner than an inline cat.

def() {
    eval "$1=\$(cat)"
}

def variable << '>>'
abc'asdf"
$(dont-execute-this)
foo"bar"''
>>

Caveat: This will strip newlines at the end of the here-doc. Newlines that are not at the end will be preserved.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文