使用 jq 获取列表项的每个类别计数
Getting per-category counts of list items with jq
我目前正在学习如何在 Linux 中将 jq 与 shell 一起使用,因为我正在为 Check_MK(以前称为 Nagios)和我的应用程序(qBittorrent用他们的 WebUI API) returns JSON 字符串。
目前,我已经能够仅使用一个简单的 jq length
来计算种子的总数。现在,我想计算当前正在下载、播种或暂停的种子的数量。我只对 state
感兴趣,所以如果我有 6 个种子,我的 JSON 可能看起来像这样:
[
{
"state": "uploading"
},
{
"state": "downloading"
},
{
"state": "downloading"
},
{
"state": "downloading"
},
{
"state": "pauseDL"
},
{
"state": "pauseUP"
}
]
这里,jq length
returns 6. 3正在下载,1正在上传,2暂停,0错误,我需要怎么做?
这是我的实际脚本:
#!/bin/sh
curl -s http://localhost:8080/query/torrents -o /tmp/torrents.json
count=$(jq length /tmp/torrents.json)
echo "0 qbt_Nb_torrents - $count"
Check_MK 需要 echo
的语法(如 here 所述)。
我已经阅读了多个有关过滤器的示例,但当我们通过顶级属性进行过滤时,它们似乎都有效。在这里,我的顶级基本上只是 [0],...,[5],所以它不适用于我在手册中找到的示例。
附加信息
WebUI API 表示有 12 种不同的可能状态。这就是我打算将它们分开的方式:
downloading: queuedDL, checkingDL, downloading
uploading: queuedUP, checkingUP, uploading
pause: pausedUP, pausedDL
error: error
stalled: stalledUP, stalledDL, metaDL
根据 CheckMK 语法,我基本上需要输出如下内容:
0 qbt_Nb_torrents - 6 total, 3 downloading, 1 seeding, 2 on pause, 0 stalled, 0 error
开头的第一个0表示CheckMK的OK状态。如果有任何停滞的种子,我希望该状态变为 1,如果有任何种子错误,则状态变为 2。示例:
2 qbt_Nb_torrents - 8 total, 3 downloading, 1 seeding, 2 on pause, 1 stalled, 1 error
对于有相关问题但未分享 OP 的具体要求的其他人:请参阅编辑历史记录!在此答案的先前迭代中,还有其他几个相关提案,包括 group_by
使用。
如果您需要 所有 值的条目,即使是没有出现的条目,您可以考虑:
jq -r '
def filterStates($stateMap):
if $stateMap[.] then $stateMap[.] else . end;
def errorLevel:
if (.["error"] > 0) then 2 else
if (.["stalled"] > 0) then 1 else
0
end
end;
{"queuedDL": "downloading",
"checkingDL": "downloading",
"queuedUP": "uploading",
"checkingUP": "uploading",
"pausedUP": "pause",
"pausedDL": "pause",
"stalledUP": "stalled",
"stalledDL": "stalled",
"metaDL": "stalled"} as $stateMap |
# initialize an output array since we want 0 outputs for everything
{"pause": 0, "stalled": 0, "error": 0, "downloading": 0, "uploading": 0} as $counts |
# count number of items which filter to each value
reduce (.[].state | filterStates($stateMap)) as $state ($counts; .[$state]+=1) |
# actually format an output string
"\(. | errorLevel) qbt_Nb_torrents - \(values | add) total, \(.["downloading"]) downloading, \(.["uploading"]) seeding, \(.["pause"]) on pause, \(.["stalled"]) stalled, \(.["error"]) error"
' /tmp/torrents.json
以防万一有人想知道我在 Charles Duffy 的出色回答之后到底使用了什么,这里是完整的 /usr/lib/check_mk_agent/local/qbittorrent
shell 脚本,它允许 Check_MK (1.5.0 raw ) 以获取我认为与我的 qBittorrent 应用程序 (qBittorrent v3.3.7 Web UI) 运行 在我服务器上的专用 VM 中最相关的信息:
#!/bin/sh
curl -s http://localhost:8080/query/transferInfo -o /tmp/transferInfo.json
curl -s http://localhost:8080/query/torrents -o /tmp/torrents.json
if [ -e /tmp/transferInfo.json ]
then
dwl=$(jq .dl_info_speed /tmp/transferInfo.json)
dwl_MB=$(bc <<< "scale=2;$dwl/1048576")
upl=$(jq .up_info_speed /tmp/transferInfo.json)
upl_MB=$(bc <<< "scale=2;$upl/1048576")
echo "0 qbt_Global_speed download=$dwl_MB|upload=$upl_MB Download: $dwl_MB MB/s, Upload: $upl_MB MB/s"
rm -f /tmp/transferInfo.json
else
echo "3 qbt_Global_speed download=0|upload=0 Can't get the information from qBittorrent WebUI API"
fi
if [ -e /tmp/torrents.json ]
then
jq -r '
def filterStates($stateMap):
if $stateMap[.] then $stateMap[.] else . end;
{"queuedDL": "downloading",
"checkingDL": "downloading",
"queuedUP": "uploading",
"checkingUP": "uploading",
"pausedUP": "pause",
"pausedDL": "pause",
"stalledUP": "stalled",
"stalledDL": "stalled",
"metaDL": "stalled"} as $stateMap |
# initialize an output array since we want 0 outputs for everything
{"pause": 0, "stalled": 0, "error": 0, "downloading": 0, "uploading": 0} as $counts |
# count number of items which filter to each value
reduce (.[].state | filterStates($stateMap)) as $state ($counts; .[$state]+=1) |
# output string
"P qbt_Nb_torrents total=\(values|add)|downloading=\(.["downloading"])|seeding=\(.["uploading"])|pause=\(.["pause"])|stalled=\(.["stalled"]);0|error=\(.["error"]);0;0 total is \(values|add), downloading is \(.["downloading"]), seeding is \(.["uploading"]), pause is \(.["pause"])"
' /tmp/torrents.json
rm -f /tmp/torrents.json
else
echo "3 qbt_Nb_torrents total=0|downloading=0|seeding=0|pause=0|stalled=0;0|error=0;0;0 Can't get the information from qBittorrent WebUI API"
fi
这是 1 个停滞的 torrent 的输出:
0 qbt_Global_speed download=0|upload=0 Download: 0 MB/s, Upload: 0 MB/s
P qbt_Nb_torrents total=1|downloading=0|seeding=0|pause=0|stalled=1;0|error=0;0;0 total is 1, downloading is 0, seeding is 0, pause is 0
由于 Check_MK 的工作方式,我认为我需要的 errorLevel
(请参阅 Charles 的回答)不是必需的;当指定警告和临界值时,它通过 the metric parameter 自行处理阈值。
这是 Check_MK 中的样子:
注意 Check_MK 如何自动添加停滞和错误的种子文件。这是因为指定了警告 and/or 临界阈值。一般指标(有或没有阈值)对于提供详细的相关图表很重要。
这是@CharlesDuffy 解决方案的模块化版本。主要兴趣点可能是通用 "bag of words" 过滤器:
# bag of words
def bow(init; s): reduce s as $word (init; .[$word] += 1) ;
还要注意初始化函数:
# initialize an output object since we minimally want 0s
def init:
{} | {pause,stalled,error,downloading,uploading} | map_values(0);
有了这些额外的抽象,"main" 程序就变成了两行代码。
def filterStates($stateMap):
if $stateMap[.] then $stateMap[.] else . end ;
def errorLevel:
if .error > 0 then 2
elif .stalled > 0 then 1
else 0
end ;
def stateMap:
{"queuedDL": "downloading",
"checkingDL": "downloading",
"queuedUP": "uploading",
"checkingUP": "uploading",
"pausedUP": "pause",
"pausedDL": "pause",
"stalledUP": "stalled",
"stalledDL": "stalled",
"metaDL": "stalled"} ;
"Main"
# count number of items which map to each value
bow(init; .[].state | filterStates(stateMap))
# format an output string
| "\(errorLevel) qbt_Nb_torrents - \(values | add) total, \(.downloading) downloading, \(.uploading) seeding, \(.pause) on pause, \(.stalled) stalled, \(.error) error"
我目前正在学习如何在 Linux 中将 jq 与 shell 一起使用,因为我正在为 Check_MK(以前称为 Nagios)和我的应用程序(qBittorrent用他们的 WebUI API) returns JSON 字符串。
目前,我已经能够仅使用一个简单的 jq length
来计算种子的总数。现在,我想计算当前正在下载、播种或暂停的种子的数量。我只对 state
感兴趣,所以如果我有 6 个种子,我的 JSON 可能看起来像这样:
[
{
"state": "uploading"
},
{
"state": "downloading"
},
{
"state": "downloading"
},
{
"state": "downloading"
},
{
"state": "pauseDL"
},
{
"state": "pauseUP"
}
]
这里,jq length
returns 6. 3正在下载,1正在上传,2暂停,0错误,我需要怎么做?
这是我的实际脚本:
#!/bin/sh
curl -s http://localhost:8080/query/torrents -o /tmp/torrents.json
count=$(jq length /tmp/torrents.json)
echo "0 qbt_Nb_torrents - $count"
Check_MK 需要 echo
的语法(如 here 所述)。
我已经阅读了多个有关过滤器的示例,但当我们通过顶级属性进行过滤时,它们似乎都有效。在这里,我的顶级基本上只是 [0],...,[5],所以它不适用于我在手册中找到的示例。
附加信息
WebUI API 表示有 12 种不同的可能状态。这就是我打算将它们分开的方式:
downloading: queuedDL, checkingDL, downloading
uploading: queuedUP, checkingUP, uploading
pause: pausedUP, pausedDL
error: error
stalled: stalledUP, stalledDL, metaDL
根据 CheckMK 语法,我基本上需要输出如下内容:
0 qbt_Nb_torrents - 6 total, 3 downloading, 1 seeding, 2 on pause, 0 stalled, 0 error
开头的第一个0表示CheckMK的OK状态。如果有任何停滞的种子,我希望该状态变为 1,如果有任何种子错误,则状态变为 2。示例:
2 qbt_Nb_torrents - 8 total, 3 downloading, 1 seeding, 2 on pause, 1 stalled, 1 error
对于有相关问题但未分享 OP 的具体要求的其他人:请参阅编辑历史记录!在此答案的先前迭代中,还有其他几个相关提案,包括 group_by
使用。
如果您需要 所有 值的条目,即使是没有出现的条目,您可以考虑:
jq -r '
def filterStates($stateMap):
if $stateMap[.] then $stateMap[.] else . end;
def errorLevel:
if (.["error"] > 0) then 2 else
if (.["stalled"] > 0) then 1 else
0
end
end;
{"queuedDL": "downloading",
"checkingDL": "downloading",
"queuedUP": "uploading",
"checkingUP": "uploading",
"pausedUP": "pause",
"pausedDL": "pause",
"stalledUP": "stalled",
"stalledDL": "stalled",
"metaDL": "stalled"} as $stateMap |
# initialize an output array since we want 0 outputs for everything
{"pause": 0, "stalled": 0, "error": 0, "downloading": 0, "uploading": 0} as $counts |
# count number of items which filter to each value
reduce (.[].state | filterStates($stateMap)) as $state ($counts; .[$state]+=1) |
# actually format an output string
"\(. | errorLevel) qbt_Nb_torrents - \(values | add) total, \(.["downloading"]) downloading, \(.["uploading"]) seeding, \(.["pause"]) on pause, \(.["stalled"]) stalled, \(.["error"]) error"
' /tmp/torrents.json
以防万一有人想知道我在 Charles Duffy 的出色回答之后到底使用了什么,这里是完整的 /usr/lib/check_mk_agent/local/qbittorrent
shell 脚本,它允许 Check_MK (1.5.0 raw ) 以获取我认为与我的 qBittorrent 应用程序 (qBittorrent v3.3.7 Web UI) 运行 在我服务器上的专用 VM 中最相关的信息:
#!/bin/sh
curl -s http://localhost:8080/query/transferInfo -o /tmp/transferInfo.json
curl -s http://localhost:8080/query/torrents -o /tmp/torrents.json
if [ -e /tmp/transferInfo.json ]
then
dwl=$(jq .dl_info_speed /tmp/transferInfo.json)
dwl_MB=$(bc <<< "scale=2;$dwl/1048576")
upl=$(jq .up_info_speed /tmp/transferInfo.json)
upl_MB=$(bc <<< "scale=2;$upl/1048576")
echo "0 qbt_Global_speed download=$dwl_MB|upload=$upl_MB Download: $dwl_MB MB/s, Upload: $upl_MB MB/s"
rm -f /tmp/transferInfo.json
else
echo "3 qbt_Global_speed download=0|upload=0 Can't get the information from qBittorrent WebUI API"
fi
if [ -e /tmp/torrents.json ]
then
jq -r '
def filterStates($stateMap):
if $stateMap[.] then $stateMap[.] else . end;
{"queuedDL": "downloading",
"checkingDL": "downloading",
"queuedUP": "uploading",
"checkingUP": "uploading",
"pausedUP": "pause",
"pausedDL": "pause",
"stalledUP": "stalled",
"stalledDL": "stalled",
"metaDL": "stalled"} as $stateMap |
# initialize an output array since we want 0 outputs for everything
{"pause": 0, "stalled": 0, "error": 0, "downloading": 0, "uploading": 0} as $counts |
# count number of items which filter to each value
reduce (.[].state | filterStates($stateMap)) as $state ($counts; .[$state]+=1) |
# output string
"P qbt_Nb_torrents total=\(values|add)|downloading=\(.["downloading"])|seeding=\(.["uploading"])|pause=\(.["pause"])|stalled=\(.["stalled"]);0|error=\(.["error"]);0;0 total is \(values|add), downloading is \(.["downloading"]), seeding is \(.["uploading"]), pause is \(.["pause"])"
' /tmp/torrents.json
rm -f /tmp/torrents.json
else
echo "3 qbt_Nb_torrents total=0|downloading=0|seeding=0|pause=0|stalled=0;0|error=0;0;0 Can't get the information from qBittorrent WebUI API"
fi
这是 1 个停滞的 torrent 的输出:
0 qbt_Global_speed download=0|upload=0 Download: 0 MB/s, Upload: 0 MB/s
P qbt_Nb_torrents total=1|downloading=0|seeding=0|pause=0|stalled=1;0|error=0;0;0 total is 1, downloading is 0, seeding is 0, pause is 0
由于 Check_MK 的工作方式,我认为我需要的 errorLevel
(请参阅 Charles 的回答)不是必需的;当指定警告和临界值时,它通过 the metric parameter 自行处理阈值。
这是 Check_MK 中的样子:
注意 Check_MK 如何自动添加停滞和错误的种子文件。这是因为指定了警告 and/or 临界阈值。一般指标(有或没有阈值)对于提供详细的相关图表很重要。
这是@CharlesDuffy 解决方案的模块化版本。主要兴趣点可能是通用 "bag of words" 过滤器:
# bag of words
def bow(init; s): reduce s as $word (init; .[$word] += 1) ;
还要注意初始化函数:
# initialize an output object since we minimally want 0s
def init:
{} | {pause,stalled,error,downloading,uploading} | map_values(0);
有了这些额外的抽象,"main" 程序就变成了两行代码。
def filterStates($stateMap):
if $stateMap[.] then $stateMap[.] else . end ;
def errorLevel:
if .error > 0 then 2
elif .stalled > 0 then 1
else 0
end ;
def stateMap:
{"queuedDL": "downloading",
"checkingDL": "downloading",
"queuedUP": "uploading",
"checkingUP": "uploading",
"pausedUP": "pause",
"pausedDL": "pause",
"stalledUP": "stalled",
"stalledDL": "stalled",
"metaDL": "stalled"} ;
"Main"
# count number of items which map to each value
bow(init; .[].state | filterStates(stateMap))
# format an output string
| "\(errorLevel) qbt_Nb_torrents - \(values | add) total, \(.downloading) downloading, \(.uploading) seeding, \(.pause) on pause, \(.stalled) stalled, \(.error) error"