【问题标题】:Compress heredoc declaration to one line in bash?将heredoc声明压缩到bash中的一行?
【发布时间】:2018-10-01 18:12:53
【问题描述】:

我有这个可以在 bash 脚本中声明 JSON 字符串:

   local my_var="foobar"
   local json=`cat <<EOF
  {"quicklock":"${my_var}"}
EOF`

上面的heredoc有效,但我似乎无法以任何其他方式对其进行格式化,它实际上必须看起来完全像那个lol。

有没有办法让命令在一行,像这样:

   local json=`cat <<EOF{"quicklock":"${my_var}"}EOF`

这样会好很多,但似乎不需要,显然只是因为 EOF 不是这样工作的,我猜哈哈。

我正在寻找一种在文件中声明 JSON 的简写方式:

  1. 不需要大量的转义字符。
  2. 允许动态插值变量。

注意:我要使用的实际 JSON 有多个动态变量,其中包含许多键/值对。请推断。

【问题讨论】:

  • 为什么还要为单行文件费心呢? json='{"quicklock":true}'
  • 我的 2 美分:j='{"quicklock":"%s"}'; json=$(printf "$j" "${my_var}")。使用printf,您可以构建您的 json 骨架,然后再填充它。
  • 适用于任意数量的变量j='{"quicklock":"%s","slowlock":"%s"}'; json=$(printf "$j" "${my_var}" "${o_var}")
  • 顺便说一句,这是一种生成 JSON 的错误方式——它不会正确转义无效字符;考虑my_var='foo", "next_url": "http://evil.com/' 之类的——我见过现实世界的API,这种事情会导致向通知等添加恶意图像链接。请改用jq
  • 相比之下,@Olegzandr、json=$(jq -n --arg my_var "$my_var" '{"quicklock": $my_var}') 会尊重您的变量,但总是会生成格式良好的输出。 (如果您的变量应该是格式良好的 JSON,则可以将其更改为 json=$(jq -n --arg my_var "$my_var" '{"quicklock": ($my_var | fromjson)}');最终结果是,如果该值 不是 JSON,则您会收到错误,而不是而不是默默地生成一个无效的文档)。

标签: json bash heredoc


【解决方案1】:

我不是 JSON 专家,不太了解上面讨论中的“格式正确”参数,但是,您可以使用“here-string”而不是“here-document”,就像这样:

my_var="foobar"
json=`cat <<<{\"quicklock\":\"${my_var}\"}`

【讨论】:

  • 很好,但有一件事是真的希望避免转义字符,这是一个很大的
  • “格式良好”在此上下文中表示“符合 JSON 规范”。并非所有字符串都可以不加修改地替换到手头的模板中,同时仍确保结果是有效的 JSON。考虑my_var='"' 的一个简单示例,此代码将为其生成非格式良好的输出——输出将是{"quicklock":"""},而需要{"quicklock":"\""}。同样,对于my_var=$(printf '%s\n' "first line" "second line"),有效输出将是{"quicklock":"first line\nsecond line"},但实际 输出将有一个文字换行符。
  • @Olegzandr,它被称为“这里的字符串”。请参阅mywiki.wooledge.org/HereDocument 的“此处字符串”部分
  • @Olegzandr,当你让你的调用者负责做正确的事情时,你就是在分散责任——制造更多的碎片,如果它们的行为有问题,就会破坏系统。如果您的目标是使某些东西变得健壮,那么这不是一个好习惯,尤其是当输入中不应该允许引号或换行符不是很明显时。
  • @Olegzandr, ...这就是我的意思——如果你一开始就做对了(并使用了jq),你就不会强迫所有其他脚本需要知道什么构成一个“坏角色”,如果他们试图知道什么是坏角色,什么不是坏角色,就会有错误。
【解决方案2】:

由于没有转义字符是强要求,这里有一个基于 here-doc 的解决方案:

#!/bin/bash

my_var='foobar'

read -r -d '' json << EOF
{
    "quicklock": "$my_var"
}
EOF

echo "$json"

它将为您提供与我提到的第一个解决方案相同的输出。

请注意,如果您要将 EOF 放在双引号内:

read -r -d '' json << "EOF"

$my_var 不会被视为变量,而是纯文本,因此您会得到以下输出:

{
        "quicklock": "$my_var"
}

【讨论】:

  • 是的,真的很想避免转义字符的需要,这是一个很大的要求,我应该在问题中说明这一点。 Heredoc 允许您避免使用标准转义字符。
  • 感谢 read -d '' 解决方案 x 平台? macOS 和 linux?
  • 它是 bash 内置的,所以它应该可以在任何平台上工作。我更新了将-r 选项添加到read 的示例,因为shellcheck 报告了一个警告。
【解决方案3】:

仅使用 shell 的字符串自然连接怎么样?如果您连接 ${mybar} 而不是对其进行插值,则可以避免转义并将所有内容放在一行上:

my_var1="foobar"
my_var2="quux"
json='{"quicklock":"'${my_var1}'","slowlock":"'$my_var2'"}'

也就是说,这是一个相当粗略的方案,正如其他人指出的那样,如果变量包含引号字符,您就会遇到问题。

【讨论】:

    【解决方案4】:

    你可以用 printf 做到这一点:

    local json="$(printf '{"quicklock":"%s"}' "$my_var")"
    

    (别介意 SO 的语法高亮在这里看起来很奇怪。Posix shell 命令替换允许嵌套一层引号。)

    注释(感谢Charles Duffy's comment on the question):我假设$my_var 不受用户输入控制。如果是,您需要小心确保它对于 JSON 字符串是合法的。我强烈建议禁止使用非 ASCII 字符、双引号和反斜杠。如果您有 jq 可用,您可以按照 Charles 在 cmets 中的说明使用它,以确保您的输出格式正确。

    【讨论】:

      【解决方案5】:

      您可以定义自己的辅助函数来解决缺少 语法的情况:

      function begin() { eval echo $(sed "${BASH_LINENO[0]}"'!d;s/.*begin \(.*\) end.*/\1/;s/"/\\\"/g' "${BASH_SOURCE[0]}"); }
      

      那么你可以如下使用它。

      my_var="foobar"
      
      json=$(begin { "quicklock" : "${my_var}" } end)
      
      echo "$json"
      

      此片段显示所需的输出:

      { "quicklock" : "foobar" }
      

      这只是一个概念证明。您可以以任何您想要的方式定义您的语法(例如通过自定义 EOF 字符串结束输入,正确转义无效字符)。例如,由于 Bash 允许函数标识符使用字母数字字符以外的字符,因此可以定义这样的语法:

      json=$(/ { "quicklock" : "${my_var}" } /)
      

      此外,如果你放宽第一个标准(转义字符),普通赋值将很好地解决这个问题:

      json="{ \"quicklock\" : \"${my_var}\" }"
      

      【讨论】:

      • 有兴趣的可以详细解释一下helper函数。
      • 这很整洁
      【解决方案6】:

      为什么不使用jq?它在管理字符串插值方面非常出色,并且可以对您的结构进行 lint。

      $ echo '{}' >> foo.json
      $ local myvar="assigned-var"
      $ jq --arg ql $myvar '.quicklock=$ql' foo.json
      

      在调用 jq 的另一端出现的文本可以cat 放入文件或任何您想做的事情。文本看起来像这样:

      {"quicklock"="assigned-var"}
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-08-29
        • 1970-01-01
        • 1970-01-01
        • 2021-09-02
        • 1970-01-01
        • 2023-02-01
        相关资源
        最近更新 更多