跳转到主要内容

用于处理YAML、JSON和TOML文件的命令行工具。

项目描述

niet

Build PyPI PyPI - Python Version PyPI - Status Downloads Downloads

在您的shell中直接从YAML、JSON和TOML文件获取数据。


如何在我们的shell中轻松解析和检索YAML文件中的数据?

几年前,这个问题引导我们开发了niet。

实际上,当时我们需要一种方式来存储和检索我们自己的数据。我们创建了niet来读取这些数据。我们的目标是开发一个工具,使我们能够标准化在本地或CI管道中解析YAML的方式。我们希望它可重用且易于分发。niet应运而生。

多年来,niet不断发展,引入了对其他格式如TOML的支持。

niet类似于xmllintjq,但针对YAML、JSON和TOML数据 - 您可以使用它来切片、过滤、映射和转换结构化数据。

您可以使用简单的表达式或使用xpath高级功能来访问非平凡数据轻松检索数据。

您可以将YAML格式轻松转换为JSON或TOML格式,反之亦然。

niet是用Python编写的,因此您可以从软件包管理器(从PyPi)或直接通过克隆此存储库来安装它 - 安装它不需要特定的系统权限。

主要功能

  • 使用xpath语法提取元素
  • 从JSON、YAML和TOML格式中提取值
  • 自动检测格式(json/yaml)
  • 从网络资源读取数据
  • 从文件读取数据或将数据从stdin传递
  • 格式化输出值
  • 格式化输出以供shell eval重用
  • 将YAML转换为JSON或TOML,反之亦然

安装或更新niet

$ pip install -U niet

需求

  • Python 3.9或更高版本

支持版本

自 niet 2.0 版本起,已停止支持 Python 2.7。因此,如果您只有 Python 2.7,则可以使用早期版本(低于 2.0),但您应首先考虑这些版本将不会获得支持(无错误修复、无新功能等)。如果您报告问题或建议新功能,它们将仅针对当前版本或更高版本进行处理。

使用方法

帮助和选项

$ niet --help
usage: niet [-h] [-a ADDITIONAL_OBJECTS [ADDITIONAL_OBJECTS ...]] [-f {json,yaml,toml,eval,newline,ifs,squote,dquote,comma}] [-i] [-o OUTPUT_FILE] [-s] [-v] [--debug] object [file]

Read data from YAML or JSON file

positional arguments:
  object                Path to object. Based on jsmespath identifiers (https://jmespath.org/specification.html#identifiers) Use '.' to get whole file. (eg: a.b.c)
  file                  Optional JSON or YAML local filename or distant web resource at raw format. If not provided niet read from stdin

options:
  -h, --help            show this help message and exit
  -a ADDITIONAL_OBJECTS [ADDITIONAL_OBJECTS ...], --additional-objects ADDITIONAL_OBJECTS [ADDITIONAL_OBJECTS ...]
                        Path to additional objects to search. Here you can pass a list of additional researchs. Allow you to combine researchs into the same command call. The researchs will be made on the original file as with the
                        `object` parameter. Niet will output the results sequentially without delimiter between the results. If the `--output` argument is given by user, the results are appended at the end of the file sequentially. Based
                        on jsmespath identifiers (https://jmespath.org/specification.html#identifiers) Use '.' to get whole file. (eg: a.b.c)
  -f {json,yaml,toml,eval,newline,ifs,squote,dquote,comma}, --format {json,yaml,toml,eval,newline,ifs,squote,dquote,comma}
                        output format
  -i, --in-place        Perform modification in place. Will so alter read file
  -o OUTPUT_FILE, --output OUTPUT_FILE
                        Print output in a file instead of stdout (surcharged by in-place parameter if set)
  -s, --silent          silent mode, doesn't display message when element was not found
  -v, --version         print the Niet version number and exit (also --version)
  --debug               Activate the debug mode (based on pdb)

output formats:
  json          Return object in JSON
  yaml          Return object in YAML
  toml          Return object in TOML
  eval          Return result in a string evaluable by a shell eval command as an input
  newline       Return all elements of a list in a new line
  ifs           Return all elements of a list separated by IFS env var
  squote        Add single quotes to result
  dquote        Add double quotes to result
  comma         Return all elements separated by commas

使用来自 stdin 的 Json

$ echo '{"foo": "bar", "fizz": {"buzz": ["1", "2", "Fizz", "4", "Buzz"]}}' | niet fizz.buzz
1
2
Fizz
4
Buzz
$ echo '{"foo": "bar", "fizz": {"buzz": ["1", "2", "Fizz", "4", "Buzz"]}}' | niet fizz.buzz -f squote
'1' '2''Fizz' '4' 'Buzz'
$ echo '{"foo": "bar", "fizz": {"buzz": ["1", "2", "fizz", "4", "buzz"]}}' | niet . -f yaml
fizz:
  buzz:
  - '1'
  - '2'
  - fizz
  - '4'
  - buzz
foo: bar
$ echo '{"foo": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet "fizz.buzz[2]"
two
$ echo '{"foo": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f dquote "fizz.buzz[0:2]"
"zero" "one"
$ echo '{"foo": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f dquote "fizz.buzz[:3]"
"zero" "one" "two"

使用 YAML 文件

考虑以下内容的 yaml 文件

# /path/to/your/file.yaml
project:
    meta:
        name: my-project
    foo: bar
    list:
        - item1
        - item2
        - item3
    test-dash: value

您可以从此处下载之前的示例进行本地测试,或使用命令行进行此操作

wget https://gist.githubusercontent.com/4383/53e1599663b369f499aa28e27009f2cd/raw/389b82c19499b8cb84a464784e9c79aa25d3a9d3/file.yaml

您可以使用 niet 如下从该文件检索数据

$ niet ".project.meta.name" /path/to/your/file.yaml
my-project
$ niet ".project.foo" /path/to/your/file.yaml
bar
$ niet ".project.list" /path/to/your/file.yaml
item1 item2 item3
$ # assign return value to shell variable
$ NAME=$(niet ".project.meta.name" /path/to/your/file.yaml)
$ echo $NAME
my-project
$ niet project.'"test-dash"' /path/to/your/file.json
value

使用 JSON 文件

考虑以下内容的 json 文件

{
    "project": {
        "meta": {
            "name": "my-project"
        },
        "foo": "bar",
        "list": [
            "item1",
            "item2",
            "item3"
        ],
        "test-dash": "value"
    }
}

您可以从此处下载之前的示例进行本地测试,或使用命令行进行此操作

wget https://gist.githubusercontent.com/4383/1bab8973474625de738f5f6471894322/raw/0048cd2310df2d98bf4f230ffe20da8fa615cef3/file.json

您可以使用 niet 如下从该文件检索数据

$ niet "project.meta.name" /path/to/your/file.json
my-project
$ niet "project.foo" /path/to/your/file.json
bar
$ niet "project.list" /path/to/your/file.json
item1 item2 item3
$ # assign return value to shell variable
$ NAME=$(niet "project.meta.name" /path/to/your/file.json)
$ echo $NAME
my-project
$ niet project.'"test-dash"' /path/to/your/file.json
value

对象标识符

标识符是最基本的表达式,可以用于从 JSON/YAML 文档中提取单个元素。标识符的返回值是与标识符关联的值。如果标识符在 JSON/YAML 文档中不存在,niet 将显示特定消息并返回错误代码 1,例如

$ echo '{"foo": "bar", "fizz": {"buzz": ["1", "2", "3"]}}' | niet fizz.gogo
Element not found: fizz.gogo
$ echo $?
1

请参阅相关部分以获取有关如何使用 niet 管理错误的更多信息。

Niet 基于 jmespath 来查找结果,因此对于复杂的研究,您可以参考jmespath 规范来正确使用标识符。

如果您尝试搜索使用一些破折号的标识符,您需要用单引号和双引号包围您的搜索表达式,例如

$ echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f dquote '"foo-biz"'
bar
$ echo '{"key-test": "value"}' | niet '"key-test"'
value

然而,如果 jmespath 无法处理它,niet 将检测相关问题并自动包围您的标识符。

因此,以下示例将返回与之前示例类似的结果

$ echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f dquote foo-biz
bar
$ echo '{"key-test": "value"}' | niet key-test
value

如果您的对象不在路径的根处,例如在 tests/sample/sample.json 中,则您只需按如下方式包围要研究的标识符:project.'"test-dash"'

{
    "project": {
        "meta": {
            "name": "my-project"
        },
        "foo": "bar",
        "list": [
            "item1",
            "item2",
            "item3"
        ],
        "test-dash": "value"
    }
}

示例

niet project.'"test-dash"' tests/sample/sample.json

使用jmespath 标识符的更多示例。

附加对象

附加对象允许您组合多个查询。--additional-objects 参数接受一个对象字符串列表。这些对象与用作查询输入的基本对象相同。

此参数允许从输入生成高级输出,让我们看一个示例

考虑以下 yaml 示例

configuration:
  warehouse: warehouse-name
  database: database-name
  object_type:
      schema:
        schema1: "/path/to/schema1.sql"
        schema2: "/path/to/schema2.sql"

以下命令将允许我们生成由用作查询的两个对象的结果组成的输出

$ niet ".configuration.object_type | keys(@)[0]" config.yaml
-a ".configuration.object_type.schema.[keys(@)[0], values(@)[0]]"

上述命令将输出

schema
schema1
/path/to/schema1.sql

如果没有结合两个查询的结果,则不可能产生此输出,附加对象正是为此而设计的。

这些附加对象的输出将按照命令行中给出的顺序依次打印。

输出

标准输出

默认情况下,niet 将输出打印到 stdout。

将输出保存到文件

您可以使用 -o 或 --output 参数传递文件名,直接写入文件。如果不存在,将创建该文件;如果已存在,将替换该文件。

文件内修改

您可以使用 -i 或 --in-place 参数直接修改文件。这将用 niet 命令的输出替换输入文件。这可以用于从文件中提取一些数据或重排文件。

输出格式

您可以使用 -f 或 --format 可选参数更改输出格式。

默认情况下,niet 侦测输入格式并使用相同格式显示复杂对象。如果对象是列表或值,则使用换行符输出格式。

输出格式有

  • ifs
  • squote
  • dquote
  • newline
  • yaml
  • json
  • toml

ifs

IFS 输出格式在一行中打印列表的所有值或单个值。如果定义了 IFS 环境变量,则所有值由 IFS 内容分隔,否则由空格分隔。

示例(考虑前面的 YAML 文件示例

$ IFS="|" niet .project.list /path/to/your/file.yaml -f ifs
item1|item2|item3
$ IFS=" " niet .project.list /path/to/your/file.yaml -f ifs
item1 item2 item3
$ IFS="@" niet .project.list /path/to/your/file.yaml -f ifs
item1@item2@item3

这在 shell 循环中非常有用,但当然,您的内容不能包含 IFS 值

OIFS="$IFS"
IFS="|"
for i in $(niet .project.list /path/to/your/file.yaml -f ifs); do
    echo ${i}
done
IFS="${OIFS}"

前面的示例提供了以下输出

item1
item2
item3

有关单引号输出,请参阅 squote 输出或 dquote 双引号输出与 IFS

squote

单引号输出格式在一行中打印列表的所有值或单个值。所有值都使用单引号引起来,并由 IFS 值分隔。

示例(考虑前面的 YAML 文件示例

$ # With the default IFS
$ niet .project.list /path/to/your/file.yaml -f squote
'item1' 'item2' 'item3'
$ # With a specified IFS
$ IFS="|" niet .project.list /path/to/your/file.yaml -f squote
'item1'|'item2'|'item3'

dquote

双引号输出格式在一行中打印列表的所有值或单个值。所有值都使用双引号引起来,并由 IFS 值分隔。

示例(考虑前面的 YAML 文件示例

$ # With the default IFS
$ niet .project.list /path/to/your/file.yaml -f dquote
'item1' 'item2' 'item3'
$ # With a specified IFS
$ IFS="|" niet .project.list /path/to/your/file.yaml -f dquote
"item1"|"item2"|"item3"

newline

newline 输出格式按行打印列表的一个值或单个值。

newline 格式主要用于 shell while read 循环和脚本交互。

示例

while read value: do
    echo $value
done < $(niet --format newline project.list your-file.json)

逗号

comma 输出格式在同一行上打印结果,并用逗号分隔。

comma 格式允许您将输出格式化为其他命令行接口可以消费的结果。例如,某些参数解析器允许您为同一参数传递多个值(例如,beagle 命令 允许您 重复 --repo 选项)。

beagle 和 shell 集成示例

$ OSLO_PROJECTS_URL=https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml
$ beagle search \
    -f link \
    --repo $(niet "oslo.deliverables.*.repos[0]" ${OSLO_PROJECTS_URL} -f comma) 'venv'

前面的命令将返回包含 openstack oslo 项目(pbr、taskflow、oslo.messaging 等)中 venv 的所有文件的链接。

另一个范围更小的 openstack oslo 项目

$ niet "oslo.deliverables.*.repos[0][?contains(@, \`oslo\`) == \`true\`]" \
    https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml \
    -f comma
openstack/oslo-cookiecutter,openstack/oslo-specs,openstack/oslo.cache,
openstack/oslo.concurrency,openstack/oslo.config,openstack/oslo.context,
openstack/oslo.db,openstack/oslo.i18n,openstack/oslo.limit,openstack/oslo.log,
openstack/oslo.messaging,openstack/oslo.middleware,
openstack/oslo.policy,openstack/oslo.privsep,openstack/oslo.reports,
openstack/oslo.rootwrap,openstack/oslo.serialization,openstack/oslo.service,
openstack/oslo.tools,openstack/oslo.upgradecheck,openstack/oslo.utils,
openstack/oslo.versionedobjects,openstack/oslo.vmware,openstack/oslotest

在前面的示例中,我们只检索名称中包含 oslo 的项目仓库,因此像 taskflowpbr 等其他项目将被忽略。

eval

eval 输出格式允许您将输出字符串评估为从您的 JSON/YAML 内容生成的 shell 变量的初始化。

您可以从整个内容初始化 shell 变量,例如

$ echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f eval .
 foo_biz="bar";fizz__buzz=( zero one two three )
$ eval $(echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f eval .)
$ echo ${foo_biz}
bar
$ echo ${fizz__buzz}
zero one two three
$ eval $(echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f eval '"foo-biz"'); echo ${foo_biz}
bar
$ echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f eval fizz.buzz
fizz_buzz=( zero one two three );

父元素由 __ 分隔,例如,fizz.buzz 元素将表示为名为 fizz__buzz 的变量。您需要在调用预期变量时考虑这一点。

您还可以从内容中初始化一些 shell 数组,并以 shell 方式遍历

$ eval $(echo '{"foo-biz": "bar", "fizz": {"buzz": ["zero", "one", "two", "three"]}}' | niet -f eval fizz.buzz)
$ for el in ${fizz_buzz}; do echo $el; done
zero
one
two
three

yaml

YAML 输出格式强制输出为 YAML,无论输入文件格式如何。

json

JSON 输出格式强制输出为 JSON,无论输入文件格式如何。

toml

TOML 输出格式强制输出为 TOML,无论输入文件格式如何。

从网络资源读取数据

Niet 允许您从通过 HTTP 协议访问的 Web 资源(在 niet 2.1 中引入)读取数据(json/yaml/toml)。

您可以通过将 URL 传递给 niet 来执行此操作,该 URL 指向原始内容(json、yaml 或 toml)。

以下是一些使用 openstack 管理的项目的数据 的示例

$ # List all the oslo projects repos (https://wiki.openstack.org/wiki/Oslo)
$ niet "oslo.deliverables.*.repos[0]" \
    https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml
openstack/automaton
openstack/castellan
...
openstack/debtcollector
...
openstack/futurist
openstack/oslo.cache
openstack/oslo.concurrency
openstack/oslo.config
openstack/oslo.context
openstack/oslo.db
openstack/oslo.i18n
openstack/oslo.limit
openstack/oslo.log
openstack/oslo.messaging
openstack/oslo.middleware
openstack/oslo.policy
...
openstack/oslo.service
openstack/osprofiler
openstack/pbr
...
openstack/stevedore
openstack/taskflow
openstack/tooz
openstack/whereto
$ niet oslo.service \
    https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml
Common libraries
$ # Get the openstack oslo's mission
$ niet oslo.mission \
    https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml
To produce a set of python libraries containing code shared by OpenStack projects.
The APIs provided by these libraries should be high quality, stable, consistent,
documented and generally applicable.
$ eval $(niet oslo.service \
    https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml -f eval) && \
    test "${oslo_service}" = "Common libraries"
$ # Get the name of the oslo PTL
$ eval $(niet oslo.ptl.name \
    https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml -f eval)
$ echo "${oslo_ptl_name}" # now display your evaluated result
$ # Convert original distant yaml file into json
$ niet . https://raw.githubusercontent.com/openstack/governance/master/reference/projects.yaml -f json

有关过滤器和选择的更多示例,请参阅 jmespath 的文档

未找到结果

默认情况下,如果没有找到结果,niet 将显示特定消息并返回错误代码 1,例如

$ echo '{"foo": "bar", "fizz": {"buzz": ["1", "2", "3"]}}' | niet fizz.gogo
Element not found: fizz.gogo
$ echo $?
1

您可以通过将 niet 传递到静默模式来避免这种行为。

静默模式允许您隐藏特定错误消息,但在找不到密钥时继续返回状态代码 1

您可以通过使用标志 -s/--silent 来使用静默模式,示例

$ echo '{"foo": "bar", "fizz": {"buzz": ["1", "2", "3"]}}' | niet fizz.gogo -s
$ echo $?
1

处理错误

当您的 JSON 文件内容无效时,niet 将显示错误并退出,返回码为 1

您可以像这样轻松保护您的脚本

PROJECT_NAME=$(niet project.meta.name your-file.yaml)
if [ "$?" = "1" ]; then
    echo "Error occur ${PROJECT_NAME}"
else
    echo "Project name: ${PROJECT_NAME}"
fi

示例

您可以通过使用项目源代码中提供的示例来尝试 niet。

以下所有示例均使用 niet 源代码中位于以下位置的示例文件 tests/samples/sample.yaml

示例示例

# tests/samples/sample.yaml
project:
    meta:
        name: my-project
    foo: bar
    list:
        - item1
        - item2
        - item3

提取单个值

检索项目名称

$ niet project.meta.name tests/samples/sample.yaml
my-project

复杂搜索

考虑以下内容

$ cat /var/lib/libvirt/dnsmasq/virbr0.status
[
  {
    "ip-address": "192.168.122.113",
    "mac-address": "52:54:00:91:14:02",
    "hostname": "rhel79",
    "expiry-time": 1644251254
  },
  {
    "ip-address": "192.168.122.162",
    "mac-address": "52:54:00:23:37:ed",
    "hostname": "satellite",
    "expiry-time": 1644251837
  }
]

这里我们想要检索当主机名为 satellite 时 ip 地址字段的值。以下命令将允许您获取此值

$ sed 's/ip/_/g' /var/lib/libvirt/dnsmasq/virbr0.status | niet "[?hostname=='satellite'].ip"
192.168.122.162

您应该注意,我们首先使用 sed 命令将 - 替换为 _。我们这样做是因为 jmespath,niet 所使用的底层库,对包含 - 的键处理得不好。我们选择将所有 - 替换为 _ 以避免在文件其他地方出现任何问题

以下是在 kvm 虚拟化实验室环境中通过在 dhcp 文件中查找 vmname 来实现自动 ssh 连接的示例,使用 niet 并执行到服务器的 ssh 连接,即使其 ip 已更改。

这里的 ssh 连接可以使用以下命令执行

ssh -o ProxyCommand='nc $(sed 's/-/_/g' /var/lib/libvirt/dnsmasq/virbr0.status | niet "[?hostname=='''%h'''].ip_address") %p' root@rhel79

提示 - 为了简化使用,例如,您可以设置此 .ssh/config 条目

host lab-*
user root
ProxyCommand /usr/bin/nc $(sed 's/-/_/g' /var/lib/libvirt/dnsmasq/virbr0.status | niet "[?hostname=='$(echo %h | cut -d'-' -f2 )'].ip_address") %p

然后执行 ssh lab-rhel79ssh lab-satellite 以连接您实验室中的所有虚拟机,通过以 lab- 为前缀的主机名。

提取列表并在 shell 中解析它

处理项目列表

$ for el in $(niet project.list tests/samples/sample.yaml); do echo ${el}; done
item1
item2
item3

您还可以使用 evalniet 输出进行评估,以设置一些可以在 shell 脚本中重用的 shell 变量,以下示例类似于上一个示例,但使用了 eval 输出格式(-f eval

$ eval $(niet -f eval project.list tests/samples/sample.yaml)
$ for el in ${project__list}; do echo $el; done
zero
one
two
three

提取复杂对象并在 shell 中解析它

将对象作为 JSON 提取以将其存储在 shell 变量中

$ project="$(niet -f json .project tests/samples/sample.yaml)"

然后在 bash 中解析它,例如

$ niet .meta.name <<< $project
my-project

将 JSON 转换为 YAML

使用 niet,您可以将 JSON 转换为 YAML,非常简单

$ niet . tests/samples/sample.json -f yaml
project:
  foo: bar
  list:
  - item1
  - item2
  - item3
  meta:
    name: my-project

将 YAML 转换为 JSON

使用 niet,您可以将 YAML 转换为 JSON,非常简单

$ niet . tests/samples/sample.yaml -f json
{
    "project": {
        "meta": {
            "name": "my-project"
        },
        "foo": "bar",
        "list": [
            "item1",
            "item2",
            "item3"
        ]
    }
}

将 JSON 转换为 TOML

使用 niet,您可以将 JSON 转换为 TOML,非常简单

$ niet . tests/samples/sample.json -f toml
[project]
foo = "bar"
list = ["item1", "item2", "item3"]
test-dash = "value"

[project.meta]
name = "my-project"

将 YAML 转换为 TOML

使用 niet,您可以将 YAML 转换为 TOML,非常简单

$ niet . tests/samples/sample.yaml -f toml
[project]
foo = "bar"
list = ["item1", "item2", "item3"]
test-dash = "value"

[project.meta]
name = "my-project"

将 TOML 转换为 YAML

使用 niet,您可以将 TOML 转换为 YAML,非常简单

niet . tests/samples/sample.toml -f yaml
project:
  foo: bar
  list:
  - item1
  - item2
  - item3
  meta:
    name: my-project
  test-dash: value

缩进 JSON 文件

以下是如何缩进 JSON 文件的示例

$ niet . tests/samples/sample_not_indented.json
{
    "project": {
        "meta": {
            "name": "my-project"
        },
        "foo": "bar",
        "list": [
            "item1",
            "item2",
            "item3"
        ],
        "test-dash": "value"
    }
}

提示

您可以将搜索项带引号或不带引号传递,如下所示

$ niet project.meta.name your-file.yaml
$ niet "project.meta.name" your-file.yaml

您可以使用调试模式逐步执行 niet。它将允许您在调试会话期间检查您的执行情况。

贡献

如果您想为 niet 贡献,请首先阅读贡献指南 请阅读贡献指南

许可证

该项目受 MIT 许可证的约束。

有关详细信息,请参阅许可证文件

项目详细信息


下载文件

下载适合您平台文件的文件。如果您不确定该选择哪个,请了解有关安装包的更多信息。

源分发

niet-3.2.0.tar.gz (20.8 kB 查看哈希值)

上传时间

构建分发

niet-3.2.0-py3-none-any.whl (14.9 kB 查看哈希值)

上传时间 Python 3

支持者