Json 如何使用shell管道更新YAML文件?

Json 如何使用shell管道更新YAML文件?,json,shell,yaml,Json,Shell,Yaml,我正在寻找一种简单的UNIX方法,用于将使用shell脚本获得的数据保存到YAML文件中,例如,我希望使用shell管道将系统中已安装软件包(RPM、pypi)的列表保存在单个YAML文件中,类似于: rpm-qa | sort |一些yaml工具manifest.yaml system.packages 预期结果将是一个manifest.yaml文件,该文件如下所示: system: packages: - xz-5.2.2-2.fc24.x86_64

我正在寻找一种简单的UNIX方法,用于将使用shell脚本获得的数据保存到YAML文件中,例如,我希望使用shell管道将系统中已安装软件包(RPM、pypi)的列表保存在单个YAML文件中,类似于:

rpm-qa | sort |一些yaml工具manifest.yaml system.packages

预期结果将是一个
manifest.yaml
文件,该文件如下所示:

system:
    packages:
        - xz-5.2.2-2.fc24.x86_64
        - xz-devel-5.2.2-2.fc24.x86_64
虽然我更喜欢YAML,但我不会拒绝JSON兼容的解决方案。我真的很想找到一种方法来使用大多数发行版上提供的工具来实现这一点,而不是需要手动或从非官方的yum/deb存储库安装的深奥的工具

如果该工具是智能的,那么当它确实存在时,它应该能够创建文件和内部路径


请注意,我必须从列表中删除
jq
工具,因为它确实存在。

虽然我还没有完全有效的解决方案,但我认为我很快就能通过
json
npm包找到一个

echo "{}" > e.json
seq 3 | xargs -0 -I XXX json -I -f e.json -e 'this.numbers=XXX'
cat e.json
现在唯一缺少的部分是如何使其使用列表并合并到列表中,而不是覆盖值。

将包名插入YAML文件中 您根本不需要为您的预期用例提供就地编辑支持。注意YAML是JSON的超集,因此
jq
的所有输出也是有效的YAML:

rpm -qa | sort | jq -Rn '{"system": {"packages": [inputs]}}'
将生成以下表单的输出文件:

{
  "system": {
    "packages": [
      "bar",
      "baz",
      "foo"
    ]
  }
}
system:
  packages:
  - bar
  - baz
  - foo
…只需一次。为了使这个YAML更为地道,您可以使用PyYAML库通过一个很小的Python垫片进行解析:

yaml_format() {
  python -c 'import sys, yaml; sys.stdout.write(yaml.dump(yaml.load(sys.stdin), default_flow_style=False))'
}
rpm -qa | sort | jq -Rn '{"system": {"packages": [inputs]}}' | yaml_format
…将生成表单的内容:

{
  "system": {
    "packages": [
      "bar",
      "baz",
      "foo"
    ]
  }
}
system:
  packages:
  - bar
  - baz
  - foo

就地更新JSON文件 假设您已经有一个JSON文件,其内容不是要保留的清单。在这种情况下,明确提出的问题变得相关。一个安全的(尽管只是GNU)实现如下所示:

atomic_update() {
  # usage: atomic_update filename command arg1 arg2 ...
  local filename tempfile retval=0
  filename=$1; shift || return
  tempfile=$(mktemp -t -- "$filename.XXXXXX") || return
  if "$@" <"$filename" >"$tempfile"; then
    # Make a best-effort attempt to preserve permissions
    chown --reference="$filename" -- "$tempfile" &>/dev/null ||:
    chmod --reference="$filename" -- "$tempfile" &>/dev/null ||:
    mv -- "$tempfile" "$filename"
  else
    retval=$?
    rm -f -- "$tempfile"
  fi
  return "$retval"
}

rpm -qa | sort | atomic_update manifest.yml jq -Rn '.system.packages = [inputs]'
原子更新(){
#用法:原子_更新文件名命令arg1 arg2。。。
本地文件名tempfile retval=0
文件名=$1;移位| |返回
tempfile=$(mktemp-t--“$filename.XXXXXX”)|返回
如果“$@”$tempfile”;则
#尽最大努力保留权限
chown--reference=“$filename”-“$tempfile”&>/dev/null | |:
chmod--reference=“$filename”-“$tempfile”&>/dev/null | |
mv--“$tempfile”“$filename”
其他的
retval=$?
rm-f--“$tempfile”
fi
返回“$retval”
}
rpm-qa | sort | atomic | u update manifest.yml jq-Rn'.system.packages=[inputs]'

jq
jq
显然不是一个选项,因为在试图实现包装器中缺少的行为时,可能会出现太多的问题。最终很容易导致文件损坏。另外,性能也是另一个问题,我不想为每个处理的行创建临时文件。谁说每个处理的行都需要一个实例?编写jq代码来读取输入流并根据该流中的多对或多个元素修改传递的内容是很简单的。为每次调用创建一个临时文件是
sed-i
(以及几乎所有其他就地编辑实例)在后台所做的。通过使用单独的工具而不是通过自己的代码来创建临时文件,并不能减少临时文件的数量。请记住,在底层系统调用接口级别,对就地编辑的支持非常有限(就地修改的内容需要在操作前后具有相同的长度,除非您正在追加或截断),因此,如果您希望
sed-i
或类似工具不创建临时文件,那么您希望它在内存中缓冲未知量的内容(在编辑过程中,内容不完整且不一致,从而危及数据的完整性)。对于需要支持大于RAM的文件的工具来说,这是不可行的。
seq 3
不使用NUL分隔符,那么
-0
-0
参数对
-xargs
有什么影响?如果不是用
-0
调用此参数,它将对每个序列号调用一次
json
。正如您指出的性能问题一样rmance在这个问题上,这让我觉得这种方法通常是不可行的(特别是当不是用于三个整数的序列,而是用于一个具有数百个包名的序列时).sea 3仅用于快速测试。我将在获得计算机的许可后立即进行测试,看起来做的是正确的事情。似乎atomic_更新部分在Mac OS上不起作用,仍在试图找出原因。抛出一些错误,因为我将重定向放在静默stderr(当您没有带有GNU扩展的chown和chmod时)在错误的位置,但它似乎在10.12.6上工作,即使使用原始代码也是如此。
printf'%s\n'2 3 1>test.txt;atomic\u update test.txt sort
test.txt
在OSX上排序(需要注意的是,如果没有GNU chmod,新的
test.txt
可以具有与旧的不同的文件权限)。