A bash one liner for getting snap data

There is always been some magic around bash one liners. They have the potential to do a lot but are incredibly hard to document, maintain and debug. For these reasons they are rarely seen in any code. I have always felt compelled by them, what can I use them for? How large could I build one?

After the snap refresh post, I felt I finally had a candidate use case: get snap information in a json structure. Also I got to use a new library (for me), jo to generate some jsons from terminal output.

And without further ado, here is the command:

snap list | awk 'NR > 1 {print $1}' | xargs -i sh -c 'snap_info=$(snap info {}) && snap_name=$(echo "$snap_info" | grep "name:" | cut -d ":" -f2) && snap_version=$(echo "$snap_info" | grep "installed:" | cut -d ":" -f2) && snap_date=$(echo "$snap_info" | grep "refresh-date:" | cut -d ":" -f2) && jo name="$(echo $snap_name)" | jo -f - version="$(echo $snap_version)" | jo -f - date="$(echo $snap_date)"' | jo -p -a >> snap_packages_summary.json

As it can be seen there are quite few things going on here, so lets break it down.

One liner objective

The objective of the command is to generate a json with data about installed snap packages, their latest update date and their version number.

Get a list of packages

snap list | awk 'NR > 1 {print $1}'

The packages name is the 1st column of the output of snap list command.

Getting more info about each snap

xargs -i sh -c 'snap_info=$(snap info {}) && snap_name=$(echo "$snap_info" | grep "name:" | cut -d ":" -f2) && snap_version=$(echo "$snap_info" | grep "installed:" | cut -d ":" -f2) && snap_date=$(echo "$snap_info" | grep "refresh-date:" | cut -d ":" -f2) && jo name="$(echo $snap_name)" | jo -f - version="$(echo $snap_version)" | jo -f - date="$(echo $snap_date)"'

Here is were most of the things happen. First there is xargs which takes the list generated in the step above and runs a function for each string. That function is a sh string command:

snap_info=$(snap info {}) && snap_name=$(echo "$snap_info" | grep "name:" | cut -d ":" -f2) && snap_version=$(echo "$snap_info" | grep "installed:" | cut -d ":" -f2) && snap_date=$(echo "$snap_info" | grep "refresh-date:" | cut -d ":" -f2) && jo name="$(echo $snap_name)" | jo -f - version="$(echo $snap_version)" | jo -f - date="$(echo $snap_date)"

The curly brackets in the first snap info {} are replaced with each snap package name. The output of the snap info command is stored and parsed. Each of the fields is retrieved with a pattern such as:

snap_name=$(echo "$snap_info" | grep "name:" | cut -d ":" -f2)

What this line does is search for the line which matches the regex “name:” then splits the line on the first occurrence of the “:” character and stores the string after “:” in the variable snap_name.

After the package name, version and update date has been obtained, a single json entry is built:

jo name="$(echo $snap_name)" | jo -f - version="$(echo $snap_version)" | jo -f - date="$(echo $snap_date)

Putting the packages info together

After the info for all the packages has been collected it is put into a list and saved to a file:

jo -p -a >> snap_packages_summary.json

Used bash syntax

Comments

The final command is indeed hard to read and understand. Clearly something you dont want your product to depend on. Also debugging one of these after 6 months will be painful. I dont think this command makes any complex stuff, but the bash syntax of a one liner makes is very hard to read. If this same one liner was to be written in a bash script it would be easier to read and understand. Even better yet, using a language such as python would make it much easier to understand.

Bonus

While working on the one liner I could not stop remembering this old video.