【问题标题】:How to use jq to convert this string to JSON?如何使用 jq 将此字符串转换为 JSON?
【发布时间】:2016-11-21 14:41:00
【问题描述】:
如何使用 jq 将以下行转换为 JSON 格式?
payment_test_service1_api[host] HOST1
payment_test_service1_api[username] USERNAME1
payment_test_service1_api[password] PASSWORD1
转成这种 JSON 格式:
"payment_test" {
"service1" :
{
"api": {
"host": "HOST1",
"username": "USERNAME1",
"password": "PASSWORD1"
}
}
}
【问题讨论】:
标签:
json
bash
shell
parsing
jq
【解决方案1】:
以下比@chepner 的解决方案更通用(它使用add,因此不限于三个键值对)并且可能稍微简单一些:
$ jq -Rn 'inputs | match("\\[(.*)\\] (.*)")
| .captures | {(.[0].string): (.[1].string)} ' tmp.txt |
jq -s '{payment_test: { service1: { api: add }}} '
现在很容易看出如何消除对 jq 的第二次调用:
jq -Rn '[ inputs | match("\\[(.*)\\] (.*)")
| .captures | {(.[0].string): (.[1].string)}]
| {payment_test: { service1: { api: add }}} ' tmp.txt
使用命名的捕获变量
一个改进是使用命名的捕获变量。 jq 提供了一个过滤器capture,用于从捕获变量中创建一个对象;它可以与from_entries 一起使用,如下所示:
[inputs | capture("\\[(?<key>.*)\\] (?<value>.*)")]
| from_entries
| {payment_test: { service1: { api: . }}}
使用这样的技术,字符串“payment_test_service1_api”也可以被解析,但是OP对此的期望还不清楚,所以最好留给另一个问答。
【解决方案2】:
更新:请参阅 @peak's answer 了解我无法理解的概括。
这远非最佳解决方案,但表明可以从任意文本生成一些 JSON。
jq -Rrn 'inputs | match("\\[(.*)\\] (.*)") | .captures[]|.string ' tmp.txt |
jq -Rn '{
payment_test: {
service1: {
api: {
"\(input)": input,
"\(input)": input,
"\(input)": input
}
}
}
}
'
以下是每个命令的细分:
-
第一个将输入读取为原始文本 (-R),在过滤器内逐行读取 (-n 以及 inputs 函数),并输出对应于的原始文本 (-r)括号中的文本和括号后面的行的其余部分。
每次在输入一个过滤器上运行第一个命令以查看完整过滤器的工作原理是有益的。 (即按顺序运行以下各项:
jq -Rrn 'inputs' tmp.txt
jq -Rrn 'inputs | match(...)' tmp.txt
jq -Rrn 'inputs | match(...) | .captures' tmp.txt
jq -Rrn 'inputs | match(...) | .captures[] | .string' tmp.txt
)
第二个命令只是读取每一行,再次作为原始文本并显式使用input,并从每一部分构建所需的对象。
应该可以将其合并为对jq 的一次调用;我在更复杂的命令方面的技能有限,但这表明您可以使用简单的部分拼凑出一个解决方案。
【解决方案3】:
每行的第一部分类似于结果中的对象结构。如果你能想出一个方案以明确的方式解析出来,你可以将它们视为路径,然后直接设置值。
假设只有三个段,其中只有第一个可能包含下划线,您可以这样做:
$ jq -Rn 'reduce (inputs
| [match("(\\S+)_([^_]+)_([^_]+)\\[([^\\]]+)\\] (\\S+)").captures[].string]
) as $p
({}; setpath($p[:-1]; $p[-1]))' input.txt