如何在bash中将json响应转换为yaml

时间:2018-11-15 09:05:59

标签: json bash shell yaml jq

我用jq从json文件中读取数据。我想将结果附加到yaml文件中,但无法正常工作。我对Shell编程很陌生。我的目标是将这些“用户”附加到yaml文件中的现有“用户”数组中。

这是我的json文件:

#$DEFAULTS_FILE

{"users":
  [
    {"name":"pi",
      "gecos": "Hypriot Pirate",
      "sudo":"ALL=(ALL) NOPASSWD:ALL",
      "shell": "/bin/bash",
      "groups":"users,docker,video",
      "plain_text_passwd":"pi",
      "lock_passwd":"false",
      "ssh_pwauth":"true",
      "chpasswd": {"expire": false}
    },
    {"name":"admin",
      "gecos": "Hypriot Pirate",
      "sudo":"ALL=(ALL) NOPASSWD:ALL",
      "shell": "/bin/bash",
      "primary-group": "users",
      "groups":"users,docker,adm,dialout,audio,plugdev,netdev,video",
      "ssh-import-id":"None",
      "plain_text_passwd":"pi",
      "lock_passwd":"true",
      "ssh_pwauth":"true",
      "chpasswd": "{expire: false}",
      "ssh-authorized-keys": ["ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local"]
    }
  ]
  }

我用它过滤它:

cat $DEFAULTS_FILE | jq .users

我不知道如何将json转换为Yaml。

我的预期结果应该是:

users:
  - name:                pi
    gecos:               "Hypriot Pirate"
    sudo:                ALL=(ALL) NOPASSWD:ALL
    shell:               /bin/bash
    groups:              users,docker,video
    plain_text_passwd:   pi
    lock_passwd:         false
    ssh_pwauth:          true
    chpasswd: { expire:  false }
  - name:                admin
    primary-group:       users
    shell:               /bin/bash
    sudo:                ALL=(ALL) NOPASSWD:ALL
    groups:              users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id:       None

我尝试使用另一个名为yq的工具,该工具类似于jq并且可以写入yaml文件。但是我没有任何积极进展。

编辑

我知道我可以通过以下方式向Yaml添加内容:

yq w -i "my.yml" "users[+]" "some content"

但是我不知道如何将json合并到其中。

任何帮助或提示都会很好,谢谢您……

10 个答案:

答案 0 :(得分:11)

使用yq version 3.3.2

cat $DEFAULTS_FILE | yq r -P -

yq的{​​{1}}的Yaml包装器

jq已读

r --prettyPrint

-P来自STDIN

答案 1 :(得分:8)

function yaml_validate {
  python -c 'import sys, yaml, json; yaml.safe_load(sys.stdin.read())'
}

function yaml2json {
  python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read())))'
}

function yaml2json_pretty {
  python -c 'import sys, yaml, json; print(json.dumps(yaml.safe_load(sys.stdin.read()), indent=2, sort_keys=False))'
}

function json_validate {
  python -c 'import sys, yaml, json; json.loads(sys.stdin.read())'
}

function json2yaml {
  python -c 'import sys, yaml, json; print(yaml.dump(json.loads(sys.stdin.read())))'
}

http://github.com/frgomes/bash-scripts上的更多Bash技巧

答案 2 :(得分:8)

yq eval -P

带有 mikefarah/yq 4.0 版(2020 年 12 月发布),可通过大多数类 Unix 操作系统包管理器安装:通过 Homebrew for macOS (brew install yq),Debian with apt ({{1 }})、Alpine 与 apt install yq (apk) 等

Working with JSON

<块引用>

要读取 json,只需传入一个 json 文件而不是 yaml,它就可以工作 - 因为 json 是 yaml 的子集。但是,您可能希望使用样式运算符或 apk add yq 标志使其看起来更像惯用的 yaml 文档。

答案 3 :(得分:4)

我不确定您要使用什么规则来达到预期的结果。似乎您是随机地将不同的规则应用于如何转换值。

据我了解,标量值仅按原样输出(使用潜在编码),对象作为键/值对输出,而数组对象的每个项目都以-输出。缩进关联什么是什么。

因此,根据这些规则,如果您要使用jq:

def yamlify:
    (objects | to_entries[] | (.value | type) as $type |
        if $type == "array" then
            "\(.key):", (.value | yamlify)
        elif $type == "object" then
            "\(.key):", "    \(.value | yamlify)"
        else
            "\(.key):\t\(.value)"
        end
    )
    // (arrays | select(length > 0)[] | [yamlify] |
        "  - \(.[0])", "    \(.[1:][])"
    )
    // .
    ;

然后使用它,将其添加到您的.jq文件中并使用它:

$ jq -r yamlify input.json
users:
  - name:       pi
    gecos:      Hypriot Pirate
    sudo:       ALL=(ALL) NOPASSWD:ALL
    shell:      /bin/bash
    groups:     users,docker,video
    plain_text_passwd:  pi
    lock_passwd:        false
    ssh_pwauth: true
    chpasswd:
        expire: false
  - name:       admin
    gecos:      Hypriot Pirate
    sudo:       ALL=(ALL) NOPASSWD:ALL
    shell:      /bin/bash
    primary-group:      users
    groups:     users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id:      None
    plain_text_passwd:  pi
    lock_passwd:        true
    ssh_pwauth: true
    chpasswd:   {expire: false}
    ssh-authorized-keys:
      - ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local

这是对齐值的另一个变体

def yamlify2:
    (objects | to_entries | (map(.key | length) | max + 2) as $w |
        .[] | (.value | type) as $type |
        if $type == "array" then
            "\(.key):", (.value | yamlify2)
        elif $type == "object" then
            "\(.key):", "    \(.value | yamlify2)"
        else
            "\(.key):\(" " * (.key | $w - length))\(.value)"
        end
    )
    // (arrays | select(length > 0)[] | [yamlify2] |
        "  - \(.[0])", "    \(.[1:][])"
    )
    // .
    ;
$ jq -r yamlify2 input.json
users:
  - name:               pi
    gecos:              Hypriot Pirate
    sudo:               ALL=(ALL) NOPASSWD:ALL
    shell:              /bin/bash
    groups:             users,docker,video
    plain_text_passwd:  pi
    lock_passwd:        false
    ssh_pwauth:         true
    chpasswd:
        expire:  false
  - name:                 admin
    gecos:                Hypriot Pirate
    sudo:                 ALL=(ALL) NOPASSWD:ALL
    shell:                /bin/bash
    primary-group:        users
    groups:               users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id:        None
    plain_text_passwd:    pi
    lock_passwd:          true
    ssh_pwauth:           true
    chpasswd:             {expire: false}
    ssh-authorized-keys:
      - ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local

答案 4 :(得分:4)

另一个内衬:

python -c 'import yaml, sys; print(yaml.dump(yaml.load(open(sys.argv[1])), default_flow_style=False))' input.json

(利用了有效的json也是有效的Yaml的事实)

然后将Yaml转换为json:

python -c 'import yaml, json, sys; print(json.dumps(yaml.load(open(sys.argv[1])), indent=2))' input.yaml

答案 5 :(得分:2)

我用ruby将json内容写入yaml。

对于您的示例,可以这样实现:

cat $DEFAULTS_FILE | jq .users | ruby -ryaml -rjson -e 'puts YAML.dump(JSON.parse(STDIN.read))' > my.yml

答案 6 :(得分:0)

我建议将yq-y选项一起使用

$ pip3 install yq # requires jq

$ cat in.json | yq -y
users:
  - name: pi
    gecos: Hypriot Pirate
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    groups: users,docker,video
    plain_text_passwd: pi
    lock_passwd: 'false'
    ssh_pwauth: 'true'
    chpasswd:
      expire: false
  - name: admin
    gecos: Hypriot Pirate
    sudo: ALL=(ALL) NOPASSWD:ALL
    shell: /bin/bash
    primary-group: users
    groups: users,docker,adm,dialout,audio,plugdev,netdev,video
    ssh-import-id: None
    plain_text_passwd: pi
    lock_passwd: 'true'
    ssh_pwauth: 'true'
    chpasswd: '{expire: false}'
    ssh-authorized-keys:
      - ssh-rsa abcdefg1234567890 YOUR_KEY@YOURHOST.local

答案 7 :(得分:0)

jq 中的解决方案(无需其他工具)

基于本文中@Jeff Mercado 的代码,我添加了对多行字符串和单引号转义的支持。

# purpose: converts Json to Yaml
# remarks:
#   You can use 'yq -y' to convert json to yaml, but ...
#     * this function can be used several times within a single jq program
#     * this function may be faster than using yq
#     * maybe yq is not available in your environment
#
# input: any Json
# output: json converted to yaml
def toYaml:
   def handleMultilineString($level):
      reduce ([match("\n+"; "g")]                       # find groups of '\n'
              | sort_by(-.offset))[] as $match
             (.; .[0:$match.offset + $match.length] +
                 "\n\("    " * $level)" +               # add one extra '\n' for every group of '\n's. Add indention for each new line
                 .[$match.offset + $match.length:]);

   def toYamlString($level):
      if type == "string"
      then handleMultilineString($level)
           | sub("'"; "''"; "g")           # escape single quotes
           | "'\(.)'"                      # wrap in single quotes
      else .
      end;

   def _toYaml($level):
      (objects | to_entries[] |
          if (.value | type) == "array" then
              "\(.key):", (.value | _toYaml($level))
          elif (.value | type) == "object" then
              "\(.key):", "\("    ")\(.value | _toYaml($level))"
          else
              "\(.key): \(.value | toYamlString($level))"
          end
      )
      // (arrays | select(length > 0)[] | [_toYaml($level)] |
          "  - \(.[0])", "\("    ")\(.[1:][])"
      )
      // .;

   _toYaml(1);

示例用法

File 'containsMultilineStrings.json'

{
  "response": {
    "code": 200,
    "message": "greeting\nthat's all folks\n\n\n"
  }
}

jq -r 'toYaml' < containsMultilineStrings.json

response:
    code: 200
    message: 'greeting

    that''s all folks



    '

jq -r 'toYaml' containsMultilineStrings.json | yq(往返)

{
  "response": {
    "code": 200,
    "message": "greeting\nthat's all folks\n\n\n"
  }
}

测试

您可以通过将 json 转换为 yaml,然后使用 yq 转换回 json 来测试函数 toYaml 的正确性。

FILE='containsMultilineStrings.json'; diff <(cat "$FILE") <(jq -r 'toYaml' $FILE | yq)

性能

快速基准测试显示,与使用 yq 相比,函数 toYaml 的运行时间减少了。 在我的电脑上,我测量了:

time for i in {1..100}; do yq -y > /dev/null < containsMultilineStrings.json; done

<块引用>

8.4 秒

time for i in {1..100}; do jq -r 'toYaml' > /dev/null containsMultilineStrings.json; done

<块引用>

3.4 秒

答案 8 :(得分:-2)

我最终安装了宝石并使用红宝石:

gem install deep_mergehttps://github.com/danielsdeleo/deep_merge

这是我的方法:

#!/usr/bin/env ruby
#
require 'json'
require 'yaml'
require 'deep_merge/rails_compat'

json_input_file = ARGF.argv[0]
yaml_output_file = ARGF.argv[1]
scope = ARGF.argv[2]

json = File.read(json_input_file)
yaml = File.read(yaml_output_file)

json_data = JSON.parse(json)
scoped_result = json_data[scope]

old_values_hash = YAML.load(File.read(yaml_output_file))
result = YAML.parse(yaml)
merged = old_values_hash.deeper_merge scoped_result

File.write(yaml_output_file, merged.to_yaml)

然后可以将其用于从json文件读取特定的scoped_value

jsonyaml.sh $JSON_FILE $YAML_TARGET "my_scope_to_add"

答案 9 :(得分:-3)

我已经可以使用以下命令获取结果:

jq -r .users defaultsfile.txt >> users.yaml

输出为清晰的Yaml格式。