Shell Cacophony
I am using jq
, qsv
, uplot
quite often. This post is to make sure that you know and use them, too. I hope you will waste as much time as I do, especially with uplot
.
Motivation
This post is for people like me who are facing a problem, reaching out to the terminal and piping random UNIX commands to solve it, but eventually regret not starting with Python or some other programming language in the first place.
I just want to make sure that we have some more shell goodies in our toolbox so that we keep doing the same thing over and over again: jq
, qsv
, uplot
.
jq
I am pretty sure that you have heard about jq
before, and most of you are already using it. But for those who are not familiar with it, jq
is a lightweight and command-line JSON processor, sort of like sed
for JSON data.
For example, use xColors API to generate 10 random colors and extract the hex codes to print them:
curl -s "https://x-colors.yurace.pro/api/random?number=10" |
jq -r ".[]|.hex"
qsv
qsv
is a command-line tool to work with CSV files. It is the successor of xsv
and is written in Rust. Current progress is quite impressive as qsv
now has SQL and Lua support.
For example, if we want to tabulate bird emojis from EmojiHub API:
curl -s "https://emojihub.yurace.pro/api/all/group/animal-bird" |
jq -r ".[]|[.name,.category,.group,.unicode[0]]|@csv" |
qsv rename --no-headers name,category,group,unicode |
qsv table
Here, we are using jq
to extract the necessary fields and output records as CSV. Then, we are using qsv
to add column names and finally tabulate.
uplot
uplot
is a command-line tool to plot data on the terminal. Be warned that it is quite addictive.
Let’s check the number of important (p <= 3
) journald
entries per day over the last 1 week:
journalctl --no-pager --since="1 week ago" --priority=3 --output=json --output-fields=__REALTIME_TIMESTAMP |
jq -r .__REALTIME_TIMESTAMP |
cut -c 1-10 |
xargs -I{} date --utc -d @{} +%Y-%m-%d |
uniq -c |
awk '{print $2","$1}' |
uplot bar -d,
Here, we are using journalctl
to get the entries with priority less than or equal to 3 in JSON format with realtime timestamp information only. Then, we are using jq
to extract the timestamp, cut
to get the date first 10 digits of the timestamp (in microseconds), xargs
to convert the timestamp to date format using date
, uniq
to count the number of entries per day, awk
to format the output as CSV, and finally uplot
to plot the data.
The output looks like this (trust me, it will look much nicer and colourful on your terminal):
┌ ┐
2024-08-22 ┤■ 1.0
2024-08-23 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 44.0
2024-08-24 ┤■■■■■ 8.0
2024-08-25 ┤■■■■■■■■■■■■■■■■■■■■■ 33.0
2024-08-26 ┤■■■■■■■■■■■■■■■■ 25.0
2024-08-27 ┤■■■■■■■■■■■■ 18.0
2024-08-28 ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 53.0
2024-08-29 ┤■■■■■■ 10.0
└ ┘
Likewise, we can get the number of entries by unit since yesterday:
journalctl --no-pager --since=yesterday --priority=3 --output=json --output-fields=_SYSTEMD_UNIT |
jq -r ._SYSTEMD_UNIT |
sort |
uniq -c |
awk '{print $2","$1}' |
uplot bar -d,
… which produces the following completely unsurprising output:
┌ ┐
bluetooth.service ┤■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ 58.0
cups.service ┤■ 1.0
⋮
null ┤■■ 4.0
systemd-coredump@0-378352-0.service ┤■ 1.0
⋮
└ ┘
Conclusion
I hope you find these tools useful.
Closing with our motto: Stick to the terminal and keep piping random UNIX commands to (not) solve our problems!
Bonus
qsv
is currently not available on nixpkgs. So, I fixed it for you:
{ stdenv
, lib
, fetchzip
, autoPatchelfHook
}:
stdenv.mkDerivation rec {
pname = "qsv";
version = "0.132.0";
src = fetchzip {
url = "https://github.com/jqnatividad/qsv/releases/download/${version}/qsv-${version}-x86_64-unknown-linux-gnu.zip";
hash = "sha256-yko+wTSGxOZWU1cJS17sPYPQeBcfyeiwQUu6dPhpL1s=";
stripRoot = false;
};
nativeBuildInputs = [
autoPatchelfHook
stdenv.cc.cc.lib
];
buildInputs = [ ];
sourceRoot = ".";
installPhase = ''
runHook preInstall
install -m755 -D source/qsvp $out/bin/qsv
runHook postInstall
'';
meta = with lib; {
homepage = "https://github.com/jqnatividad/qsv";
description = "CSVs sliced, diced & analyzed.";
platforms = platforms.linux;
};
}