【问题标题】:Convert a key:value file w/ comments into JSON document with UNIX tools使用 UNIX 工具将带有注释的键值文件转换为 JSON 文档
【发布时间】:2019-03-08 18:34:50
【问题描述】:

我在 YAML 子集中有一个文件,其中包含如下数据:

# This is a comment
# This is another comment


spark:spark.ui.enabled: 'false'
spark:spark.sql.adaptive.enabled: 'true'
yarn:yarn.nodemanager.log.retain-seconds: '259200'


我需要将其转换成如下所示的 JSON 文档(请注意,包含布尔值和整数的字符串仍然是字符串):

{
  "spark:spark.ui.enabled": "false",
  "spark:spark.sql.adaptive.enabled": "true",
  "yarn:yarn.nodemanager.log.retain-seconds", "259200"
}

我得到的最接近的是:

cat << EOF > ./file.yaml
> # This is a comment
> # This is another comment
> 
> 
> spark:spark.ui.enabled: 'false'
> spark:spark.sql.adaptive.enabled: 'true'
> yarn:yarn.nodemanager.log.retain-seconds: '259200'
> EOF
echo {$(cat file.yaml | grep -o '^[^#]*' | sed '/^$/d' | awk -F": " '{sub($1, "\"&\""); print}' | paste -sd "," -  )}

除了看起来相当粗糙并没有给出正确答案之外,它返回:

{"spark:spark.ui.enabled": 'false',"spark:spark.sql.adaptive.enabled": 'true',"dataproc:dataproc.monitoring.stackdriver.enable": 'true',"spark:spark.submit.deployMode": 'cluster'}

如果我通过管道传送到jq 会导致解析错误。

我希望我错过了一种更简单的方法,但我无法弄清楚。有人可以帮忙吗?

【问题讨论】:

  • 原谅我的天真,我认为这些工具是 bash 的一部分,很高兴得到纠正。我受到运行它的 docker 映像中可用工具的限制,该映像是从 debian 基础构建的。
  • 构建一个新的 docker 镜像,其中包含使用 YAML 和 JSON 所需的工具。
  • 我只是想处理那个特定的输入,也许提到 yaml 是一个流浪汉。我确实有jq 可用
  • 也有整数。我已经编辑了上面的示例以反映这一点
  • 如果您习惯于jq,那么还有一个名为yq 的包装器可以处理YAML:yq.readthedocs.io/en/latest

标签: bash shell awk jq


【解决方案1】:

在纯jq 中实现(使用版本 1.6 测试):

#!/usr/bin/env bash

jq_script=$(cat <<'EOF'
def content_for_line:
  "^[[:space:]]*([#]|$)" as $ignore_re |           # regex for comments, blank lines
  "^(?<key>.*): (?<value>.*)$" as $content_re |    # regex for actual k/v pairs
  "^'(?<value>.*)'$" as $quoted_re |               # regex for values in single quotes
  if test($ignore_re) then {} else                 # empty lines add nothing to the data
    if test($content_re) then (                    # non-empty: match against $content_re
      capture($content_re) as $content |           # ...and put the groups into $content
      $content.key as $key |                       # string before ": " becomes $key
      (if ($content.value | test($quoted_re)) then # if value contains literal quotes...
         ($content.value | capture($quoted_re)).value # ...take string from inside quotes
       else
         $content.value                               # no quotes to strip
       end) as $value |                     # result of the above block becomes $value
      {"\($key)": "\($value)"}              # and return a map from one key to one value
    ) else
      # we get here if a line didn't match $ignore_re *or* $content_re
      error("Line \(.) is not recognized as a comment, empty, or valid content")
    end
  end;

# iterate over our input lines, passing each one to content_for_line and merging the result
# into the object we're building, which we eventually return as our result.
reduce inputs as $item ({}; . + ($item | content_for_line))
EOF
)

# jq -R: read input as raw strings
# jq -n: don't read from stdin until requested with "input" or "inputs"
jq -Rn "$jq_script" <file.yaml >file.json

与不识别语法的工具不同,这永远不会生成不是有效 JSON 的输出;通过添加一个额外的过滤器阶段来检查和修改content_for_line 的输出,它可以很容易地使用特定于应用程序的逻辑(f/e,以数字文字而不是字符串文字发出一些值而不是其他值)进行扩展。

【讨论】:

  • 哦,我的话,我不知道它是如何工作的,但确实如此。非常感谢查尔斯。我现在将用剩下的时间来研究它并试图理解它:) 再次感谢
  • (另外,我刚刚使用我的真实 yaml 文档对其进行了测试,该文档比我在此处发布的示例大得多,效果很好)
  • 刚刚添加了一些cmets;希望他们更容易理解。如果还有其他地方需要说明,请告诉我。
【解决方案2】:

这是一个简洁但简单的解决方案:

def tidy: sub("^ *'?";"") | sub(" *'?$";"");
def kv: split(":") | [ (.[:-1] | join(":")), (.[-1]|tidy)];

reduce (inputs| select( test("^ *#|^ *$")|not) | kv) as $row ({};
    .[$row[0]] = $row[1] )

调用

jq -n -R -f tojson.jq input.txt

【讨论】:

    【解决方案3】:

    您可以使用gsubsprintfawk 中完成所有操作,例如:

    (编辑添加","分隔json记录)

    awk 'BEGIN {ol=0; print "{" } 
    /^[^#]/ { 
        if (ol) print ","
        gsub ("\047", "\042")
        $1 = sprintf ("  \"%s\":", substr ($1, 1, length ($1) - 1))
        printf "%s %s", $1, $2
        ol++ 
    } 
    END { print "\n}" }' file.yaml
    

    注意:虽然jq是json格式化的合适工具)

    说明

    • awk 'BEGIN { ol=0; print "{" } 调用awk 设置输出行 变量ol=0 用于"," 输出控制并打印标题"{"
    • /^[^#]/ { 只匹配非注释行,
    • if (ol) print "," 如果输出行ol 大于零,则输出尾随","
    • gsub ("\047", "\042") 将所有单引号替换为双引号,
    • $1 = sprintf (" \"%s\":", substr ($1, 1, length ($1) - 1)) 在第一个字段周围添加 2 个前导空格和双引号(最后一个字符除外),然后在末尾附加一个 ':'
    • print $1, $2 输出重新格式化的字段,
    • ol++ 增加输出行数,并且
    • END { print "}" }' 通过打印 "}" 页脚关闭

    使用/输出示例

    只需选择/粘贴上面的awk 命令(根据需要更改文件名)

    $ awk 'BEGIN {ol=0; print "{" }
    > /^[^#]/ {
    >     if (ol) print ","
    >     gsub ("\047", "\042")
    >     $1 = sprintf ("  \"%s\":", substr ($1, 1, length ($1) - 1))
    >     printf "%s %s", $1, $2
    >     ol++
    > }
    > END { print "\n}" }' file.yaml
    {
      "spark:spark.ui.enabled": "false",
      "spark:spark.sql.adaptive.enabled": "true"
    }
    

    【讨论】:

    • 感谢大卫,这很接近,但它不会在第一个元素之后放置逗号,因此 JSON 无效
    猜你喜欢
    • 2019-07-27
    • 2019-07-15
    • 2022-07-19
    • 1970-01-01
    • 2023-03-08
    • 2019-12-06
    • 2021-04-20
    • 2020-04-19
    • 1970-01-01
    相关资源
    最近更新 更多