Ops Scripting w. BASH: Frequency

Tracking Frequency in BASH (Bourne Again Shell): Part I

Joaquín Menchaca (智裕)
3 min readMay 15, 2019

--

Operations oriented roles, almost always require skills in automation and scripting with shell programming. These days with the ubiquity of Linux and GNU command line tools, GNU Bash has become ubiquitous.

Bash combined with command line tools, is regulated for small automation chores, data structures are supports strings, integers, and arrays. However, starting with Bash 4, you can use more advanced data structures like associative arrays (hashes, maps, or dictionaries) to create data structures that are used for algorithms like tracking frequency.

This problem and later solutions, show how to use a Bash associative array to track frequency, and along the way, show some cool tricks that you can use with Bash.

But first you need to make sure Bash 4 or higher is available on your system…

Getting Bash 4+

If you have a recent Linux distro, such as Debian, Ubuntu, CentOS, a current version of Bash 5 is already installed.

macOS

Apple’s macOS (aka Mac OS X) comes a very old 15-year old version of Bash. With Homebrew package manager, you can get a recent version of Bash:

brew install bash
sudo bash -c 'echo /usr/local/bin/bash >> /etc/shells'
chsh -s /usr/local/bin/bash

Windows

On Windows 10, we can use MSYS2 (Minimal System 2) that comes with a recent version of Bash. You can install MSYS2 with the package manager Chocolatey from either a command shell (cmd.exe) or PowerShell (powershell.exe) in Administrative mode:

choco install msys2

The Problem

The goal of this exercise is to print a summary of shell usage on a system. For this exercise, we’ll do it in two parts:

  1. Build a data structure containing the shell counts, called COUNTS from a supplied local password file ./passwd.
  2. Given the shell counts data structure COUNTS, produce a report.

The Data

Here’s the passwd file you will use for this exercise:

passwd

The Output

When generating a report, the output should look like this:

Shell Summary Report:
==================================================
Shell # of Users
----------------- ------------
/bin/bash 3 users
/bin/false 7 users
/bin/sync 1 users
/usr/sbin/nologin 17 users

The Code

Here is sample code to get you started:

Code Notes

These are some of the techniques used in the code above.

Formatted Output

We printf command (see printf), which behaves like the C-Language counterpart:

printf FORMATTED_STRING STRING1 STRING2 STRING3
printf "%s %s %s\n" STRING1 STRING2 STRING3

Repetition

You can use repetition to print out, a line for example, using this technique:

printf '=%.0s' {1..10}

This will generate ten equal symbols. This itself can be wrapped into a sub-shell $() or `` it is needs to be concatenated with another string.

Enumerating Keys

The keys and values of an associative array in Bash can be enumerated with this notation:

KEYS   = "${!ASSOC_ARRAY[@]}"
VALUES = "${ASSOC_ARRAY[@]}"

If you put these into a subshell, you then sort the keys.

SORTED_KEYS = $(echo ${![ASSOC_ARRAY[@]} | sort)

Referencing a Value

You can extract a value given a key like this:

VALUE = ${ASSOC_ARRAY[$KEY]}

Next Article

The Conclusion

For now, I’ll just present the problem, and next article I will present some solutions.

With the sample code, you can get the following takeaways for Bash

  • Creating an Associative Array with declare -A
  • Enumerating Keys and Values from an associative array
  • Referencing (looking up) an item from the associative array
  • Formatted output with printf
  • Repetition using printf "%.0s=" {1..10} trick

--

--

Joaquín Menchaca (智裕)

DevOps/SRE/PlatformEng — k8s, o11y, vault, terraform, ansible