teeny tiny bash fetch script

teeny tiny bash fetch script

This is my attempt at a neofetch, pfetch, whateverfetch style system info utility. My main concern was making something which looked nice, was easily configurable, and as portable as possible (I didn’t really try that hard with the portability). I didn’t think much about performance; I’m personally not a man who stresses too much when a command takes a quarter of a second instead of a tenth. The basic gameplan was to get an array of bash commands which would fetch various bits and bobs, then loop through this array formatting the text with ANSI escape codes. First things first, this was the associative array I came up with:

declare -A fetch=(
    [host]="$(cat /etc/hostname)"
    [uptime]="$(uptime | awk '{print $3}' | sed 's/:/h / ; s/,/m/')"
    [kernel]="$(awk '{print $3}' /proc/version)"
    [distro]="$(sed -n 's/^PRETTY_NAME="//p' /etc/os-release | sed 's/"//')"
    [shell]="$(basename $SHELL)"
    [root]="$(df -Th / | tail -n 1 | awk '{print $6}'),
            $(df -Th / | tail -n 1 | awk '{print $2}')"
    [ip]="$(host myip.opendns.com resolver1.opendns.com |
          tail -n 1 | awk '{print $4}')"
    [battery]="$(cat /sys/class/power_supply/BAT0/capacity)%"
    [cpu]="$(sed -n 5p /proc/cpuinfo | cut -d: -f2)"
    [ram]="$(free -h | sed -n 2p | awk '{print $3}') /
           $(free -h | sed -n 2p | awk '{print $2}')"
    [swap]="$(free -h | sed -n 3p | awk '{print $3}') /
            $(free -h | sed -n 3p | awk '{print $2}')"
    [display]="$(xrandr | grep '*' | awk '{print $1}'), 
    	       $(xrandr | grep '*' | awk '{print $2}' | sed 's/*/Hz/ ; s/+//')"

Each of these elements fetches a differenet piece of info. You could just use environment variables to get quite a few things (user), some were an issue of grabbing a particular piece of info from a file (distro name), and some of the more complicated ones I just reformatted output from other commands (ram usage).

Next order of business: colors. I wanted to put a chunk or randomly colored text at the start of each line so that each time you ran the command you got something that looked a little different. I made this array of escape codes each one referring to a different bold color:

declare -a colors=(
    "\e[0;1;30m" # black
    "\e[0;1;31m" # red
    "\e[0;1;32m" # green
    "\e[0;1;33m" # blue
    "\e[0;1;34m" # yellow
    "\e[0;1;35m" # pink
    "\e[0;1;36m" # magenta
    "\e[0;1;37m" # white

I then repurposed a nice function from someone on stackoverflow to get a random element from this array. The variable ‘pre’ here is the text that I want formatted:


random_color () {
    index=$(($RANDOM % $size))
    echo "${colors[$index]}${pre}\e[0m"

My plan was then to simply loop through the array, ’echo-ing’ out the random_color function, the key from the fetch array, a separator, and then the value form the fetch array. This worked mainly, the only issue being that each element from the fetch was not printed in the order it was declared. Ideally, I wanted the fetch elements to be printed in the order they were put in the array so you could configure the how they appeared. Once again my primitive understanding of bash had let me down; I turned to stackoverflow. I found the solution was to define another array containing the fetch keys and then use it to attack the other associative ‘fetch’ array:

declare -a order=(
    "uptime" # uses uptime command
    # "ip" # uses host command
    # "cpu"
    # "ram" # uses free command
    # "swap" # uses free also
    # "root" # uses df command
    # "battery"
    # "display" # uses xrandr

for info in "${order[@]}"; do
    echo -e "$(random_color) \e[0;1;3m$info\e[0m${sep}${fetch[$info]}"

This had the happy unintended consequence of allowing you to very easily configure which items you wanted in the fetch by simply commenting out keys from the order array. You can check out the script in its entirety here. This is a pretty picture of a few variations.