如何使用 JQ 在命令行中将 JSON 列表提取到 CSV 文件中?
How do I extract a JSON list into a CSV in command line using JQ?
我有以下 JSON 数据:
% cat test2
{"day":"2020-07-15","map":
{"a":"ask","b":"bid","t":"timestamp"},"msLatency":52,"pair":"EUR/USD","status":"success","ticks":[
{"b":1.14105,"a":1.14106,"x":48,"t":1594771200000},
{"b":1.14105,"a":1.14106,"x":48,"t":1594771201000},
{"b":1.14103,"a":1.14104,"x":48,"t":1594771202000},
{"b":1.141,"a":1.1413,"x":48,"t":1594771203000},
{"b":1.14103,"a":1.14104,"x":48,"t":1594771205000},
{"b":1.14094,"a":1.14095,"x":48,"t":1594778803000}],"type":"forex"}
我想得到:
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.141,1.1413,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
理想情况下,输出也应该用零填充,1 和 5 作为参数指定前两列是具有 1 个自然位和 5 个小数位的数字(尽管可以使用 [=16= 轻松完成此步骤]):
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.14100,1.14130,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
我已经用 JQ 试过了:
% cat test2 | jq '.ticks'
[
{
"b": 1.14105,
"a": 1.14106,
"x": 48,
"t": 1594771200000
},
{
"b": 1.14105,
"a": 1.14106,
"x": 48,
"t": 1594771201000
},
{
"b": 1.14103,
"a": 1.14104,
"x": 48,
"t": 1594771202000
},
{
"b": 1.141,
"a": 1.1413,
"x": 48,
"t": 1594771203000
},
{
"b": 1.14103,
"a": 1.14104,
"x": 48,
"t": 1594771205000
},
{
"b": 1.14094,
"a": 1.14095,
"x": 48,
"t": 1594778803000
}
]
但我一直不知道如何将其转换为 CSV。
编辑:
作为参考,我之前有过以下解析,这里使用JQ是更简单的选择:
cat test2 |
sed -e 's/{\"/#{\"/g' |
tr '#' '\n' |
grep -v "timestamp" |
grep -v "day" |
sed '/^[[:space:]]*$/d' |
sed 's/].*$//g' |
sed 's/{//g' |
sed 's/},//g' |
sed 's/}//g' |
sed 's/\"//g' |
awk -F '[:,]' -v decimal_places=5 -v integer_places=1 '{
for(i=1; i<=NF; i=i+2) {
value[$i]=$(i+1);
};
format_price="%0" integer_places "." decimal_places "f"
format=format_price " " format_price " %d\n"
printf(format,value["b"],value["a"],value["t"]);
}'
这是一个解决方案:
jq -r '.ticks[] | [.b, .a, .t] | join(",")' test2
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.141,1.1413,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
它不会填充到 5 位数字。我不知道如何在 jq
中做到这一点。我仍然认为另一种语言会更容易。
你完全可以为此编写自己的填充函数。首先提取小数点后的位数,并据此决定填充位数。
您可以编写一个脚本,将其命名为 script.jq
,然后执行以下操作。请注意,下面的逻辑将不适用于科学记数法中的数字。
#!/usr/bin/jq -f
def pad($len):
tostring |
match("^([0-9]*).([0-9]+)$").captures[1].length as $dec |
($len - $dec) as $l |
. + ("0" * ($l)) [:$l];
.ticks[] | [ (.b|pad(5)), (.a|pad(5)), .t ] | join(",")
并使用 jq
可执行文件
调用它
jq -r -f script.jq json
现在可以生成正确填充的输出
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.14100,1.14130,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
注意:pad($len)
的灵感来自 pkoppstein's comment from stedolan/jq - pad function #2033
这是一个惯用的 jq-only 解决方案,理解为要求小数点右边的位数(如果存在)与指定的一样。应使用 -r 命令行选项。
# format numbers whose tostring representation has a decimal point
# so that the number of digits to the right of the decimal point is $dd
# assuming $dd >= 0
def format($dd):
def rpad: (. + $dd * "0") | .[:$dd];
tostring
| index(".") as $dec
| if $dec then .[0:$dec+1] + (.[$dec+1:]|rpad)
else .
end ;
.ticks[]
| [(.b | format(1)), (.a | format(5)), .t ]
| join(",")
我有以下 JSON 数据:
% cat test2
{"day":"2020-07-15","map":
{"a":"ask","b":"bid","t":"timestamp"},"msLatency":52,"pair":"EUR/USD","status":"success","ticks":[
{"b":1.14105,"a":1.14106,"x":48,"t":1594771200000},
{"b":1.14105,"a":1.14106,"x":48,"t":1594771201000},
{"b":1.14103,"a":1.14104,"x":48,"t":1594771202000},
{"b":1.141,"a":1.1413,"x":48,"t":1594771203000},
{"b":1.14103,"a":1.14104,"x":48,"t":1594771205000},
{"b":1.14094,"a":1.14095,"x":48,"t":1594778803000}],"type":"forex"}
我想得到:
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.141,1.1413,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
理想情况下,输出也应该用零填充,1 和 5 作为参数指定前两列是具有 1 个自然位和 5 个小数位的数字(尽管可以使用 [=16= 轻松完成此步骤]):
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.14100,1.14130,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
我已经用 JQ 试过了:
% cat test2 | jq '.ticks'
[
{
"b": 1.14105,
"a": 1.14106,
"x": 48,
"t": 1594771200000
},
{
"b": 1.14105,
"a": 1.14106,
"x": 48,
"t": 1594771201000
},
{
"b": 1.14103,
"a": 1.14104,
"x": 48,
"t": 1594771202000
},
{
"b": 1.141,
"a": 1.1413,
"x": 48,
"t": 1594771203000
},
{
"b": 1.14103,
"a": 1.14104,
"x": 48,
"t": 1594771205000
},
{
"b": 1.14094,
"a": 1.14095,
"x": 48,
"t": 1594778803000
}
]
但我一直不知道如何将其转换为 CSV。
编辑: 作为参考,我之前有过以下解析,这里使用JQ是更简单的选择:
cat test2 |
sed -e 's/{\"/#{\"/g' |
tr '#' '\n' |
grep -v "timestamp" |
grep -v "day" |
sed '/^[[:space:]]*$/d' |
sed 's/].*$//g' |
sed 's/{//g' |
sed 's/},//g' |
sed 's/}//g' |
sed 's/\"//g' |
awk -F '[:,]' -v decimal_places=5 -v integer_places=1 '{
for(i=1; i<=NF; i=i+2) {
value[$i]=$(i+1);
};
format_price="%0" integer_places "." decimal_places "f"
format=format_price " " format_price " %d\n"
printf(format,value["b"],value["a"],value["t"]);
}'
这是一个解决方案:
jq -r '.ticks[] | [.b, .a, .t] | join(",")' test2
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.141,1.1413,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
它不会填充到 5 位数字。我不知道如何在 jq
中做到这一点。我仍然认为另一种语言会更容易。
你完全可以为此编写自己的填充函数。首先提取小数点后的位数,并据此决定填充位数。
您可以编写一个脚本,将其命名为 script.jq
,然后执行以下操作。请注意,下面的逻辑将不适用于科学记数法中的数字。
#!/usr/bin/jq -f
def pad($len):
tostring |
match("^([0-9]*).([0-9]+)$").captures[1].length as $dec |
($len - $dec) as $l |
. + ("0" * ($l)) [:$l];
.ticks[] | [ (.b|pad(5)), (.a|pad(5)), .t ] | join(",")
并使用 jq
可执行文件
jq -r -f script.jq json
现在可以生成正确填充的输出
1.14105,1.14106,1594771200000
1.14105,1.14106,1594771201000
1.14103,1.14104,1594771202000
1.14100,1.14130,1594771203000
1.14103,1.14104,1594771205000
1.14094,1.14095,1594778803000
注意:pad($len)
的灵感来自 pkoppstein's comment from stedolan/jq - pad function #2033
这是一个惯用的 jq-only 解决方案,理解为要求小数点右边的位数(如果存在)与指定的一样。应使用 -r 命令行选项。
# format numbers whose tostring representation has a decimal point
# so that the number of digits to the right of the decimal point is $dd
# assuming $dd >= 0
def format($dd):
def rpad: (. + $dd * "0") | .[:$dd];
tostring
| index(".") as $dec
| if $dec then .[0:$dec+1] + (.[$dec+1:]|rpad)
else .
end ;
.ticks[]
| [(.b | format(1)), (.a | format(5)), .t ]
| join(",")