I'm currently learning how to use jq with shell in Linux since I'm developing custom checks for Check_MK (formerly known as Nagios) and my application (qBittorrent with their WebUI API) returns JSON strings.
Currently, I'm already able to count the total number of torrents just by using a simple jq length
. Now, I would like to count the number of torrents that are currently dowloading, seeding or on pause. I'm only interested by the state
, so if I have 6 torrents, my JSON could look like that:
[
{
"state": "uploading"
},
{
"state": "downloading"
},
{
"state": "downloading"
},
{
"state": "downloading"
},
{
"state": "pauseDL"
},
{
"state": "pauseUP"
}
]
Here, jq length
returns 6. What do I need to do to get the details such as 3 are downloading, 1 is uploading, 2 are paused and 0 are in error?
Here is my actual script:
#!/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"
The syntax for the echo
is required by Check_MK (as explained here).
I've read multiple examples on filters but they all seem to be working when we're filtering through the top-level attributes. Here, my top level is basically just [0], ..., [5], so it doesn't work with the examples I've found in the manual.
Additional information
The WebUI API says there are 12 different possible states. That's how I intend to split them up:
downloading: queuedDL, checkingDL, downloading
uploading: queuedUP, checkingUP, uploading
pause: pausedUP, pausedDL
error: error
stalled: stalledUP, stalledDL, metaDL
As per the CheckMK syntax, I need to basically output something like:
0 qbt_Nb_torrents - 6 total, 3 downloading, 1 seeding, 2 on pause, 0 stalled, 0 error
The first 0 at the beginning means an OK status for CheckMK. If there are any stalled torrents, I want that status to become 1, and if there is any torrent in error, the status becomes 2. Example:
2 qbt_Nb_torrents - 8 total, 3 downloading, 1 seeding, 2 on pause, 1 stalled, 1 error
For others with related questions, but not sharing the OP's specific requirements: See edit history! There are several other relevant proposals, including group_by
use, in prior iterations of this answer.
If you need entries for all values, even ones which have no occurrence, you might consider:
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