Maciej Walkowiak

Beautiful bash scripts with Gum

Russia has invaded Ukraine and already killed tens of thousands of civilians, with many more raped and tortured. Ukraine needs your help!

Help Ukraine Now!

Bash scripts don't need to be boring. I found a fantastic tool that make it easy to use great looking UI elements in bash - Gum.

I have used Gum to create sinit - shiny command line Spring Boot project initialzer - that one day got surprisingly popular on Twitter.

Gum

Gum is a part of bigger project - Charm.sh - a set of Go libraries for making command line apps. Gum is not a library for Go though. It's a simple tool that lets you use the power of Charm - specifically Bubbles and Lipgloss in bash scripts.

How to use it? It is surprisingly easy.

First, install gum:

$ brew install gum

Installation instructions for other operating systems.

Now you can run gum command from terminal to get beautiful prompts:

Gum demo

There are multiple widgets like choose: input, file, writer, spin and more. All are presented in Gum readme.

Since they are regular command, they can be also used in bash scripts:

#!/bin/sh
gum style --foreground 255 'Choose fruit'
FRUIT=$(gum choose "Banana" "Apple" "Orange")
gum confirm "Pack to shopping cart?" && gum spin --title "Adding to shopping cart" -- sleep 2 && echo "Added $FRUIT to shopping cart"

Results in:

Gum demo

While Gum is trivial to use but few things took me a bit of time to figure out.

Tips & Tricks

Printing mixed style content

To print a styled content use gum style:

gum style --foreground 255 'Choose fruit'

If you want to use multiple styles in single line:

echo "Hello! $(gum style --foreground 240 'Choose') $(gum style --foreground 212 'fruit')"

Save a result of gum confirm in a variable

gum confirm does not return the result to a variable but we can access it through $?:

gum confirm "Pack to shopping cart?"

if [ $? -eq 0 ]; then
  ...
else
  ...
fi

where $? == 0 for choosing Yes and $? == 1 for choosing No.

Save a result of gum spin in a variable

By default gum spin executes a command but does not return the results. To fix it add --show-output parameter:

QUEUES=$(gum spin --show-output --title "Fetching SQS queues" -- aws sqs list-queues | jq -r '.QueueUrls[]')

More complex example

This is a bit more complex example showing how to use gum in a real world use case - it's a CLI listener for SQS, useful for debugging incoming messages:

QUEUES=$(gum spin --show-output --title "Fetching SQS queues" -- aws sqs list-queues | jq -r '.QueueUrls[]')
QUEUE_URL=$(gum choose $QUEUES)
echo $SELECTED_QUEUE

while true; do
	MSG=$(gum spin --show-output --title "receiving messages" -- aws sqs receive-message --queue-url $QUEUE_URL --wait-time-seconds 20)
    RECEIPT_HANDLE=$(echo $MSG | jq -r '.Messages[] | .ReceiptHandle')
    if ! [[ -z ${RECEIPT_HANDLE} ]]; then
        echo "Received message:"
        echo "$MSG" | jq -r '.Messages[] | .Body | fromjson'
        gum spin --title "deleting message" -- aws sqs delete-message --queue-url $QUEUE_URL --receipt-handle $RECEIPT_HANDLE
    fi
done

That's how it works in action:

Gum demo SQS

Conclusion

While Gum is great for making boring bash scripts elegant and interactive, writing full-fledged complex CLIs in bash is a pain. If you are comfortable with Go, go ahead and code something cool with Bubbles and Lipgloss.

Ping me on Twitter if you create something cool with gum! I would love to see how much creativity it unlocks.