Tame your pesky little scripts
This blog post started its life as a lightning talk that I'm doing today at Aurajoki Overflow's afterwork. As I worked on the talk, I realised I had a more coherent story to tell than I had already planned so now it gets a new life as a blog post as well.
"Pesky little scripts" — a term I stole from Redowan Delowar — are all those shell scripts and fish functions and shell aliases you write to make your own life easier. I don't know about you, but I have quite a few of them and the collection keeps growing.
But they can get messy to organise, to remember and to run. Especially when you start to add more than just shell scripts. My collection is a messy amalgamation of bash, zsh, Python, Javascript and Rust scripts. Some of them I use daily, others maybe once every two months.
There are a couple of main ideas that have had a big impact on how I manage them and I've noticed that the amount of scripts and aliases I write has grown immensly after I adopted these.
Namespacing
In PyCon Argentina 2018, Brandon Rhodes delivered a wonderful keynote Activation Energy. In it, he talked about his journey to figure out ways to reduce the activation energy that is needed to do things with software.
One of the these things was prefixing his custom shell scripts so they would be easy to find.
He started by looking at all the characters he could start his scripts, eliminating A-Z and 0-9 as they are commonly used and then eliminating all characters that have special meaning in bash shell.
He ended up with 6 options:
-
@,_,+and:which all required using a Shift key -
-which is a named command in bash -
,which was the perfect prefix
-> , [tab]
,analytics ,tcg
,bookmarklet ,webmention
,confetti ,webp
,ptest ,zine
By typing , and hitting tab, I get
autocompletions for all my scripts and aliases. As long as their names remind
me what they do, this is a big improvement over having to think and remember
what you called you script and then wading through autocompletions of all the
built-in or 3rd party commands that have been installed in your system.
Shebang (#!)
Namespacing helps with remembering and finding your scripts but equally
crucial bit are
shebangs. A shebang is when your script starts with
#! and it tells the shell which software
you want to execute this script with.
You may have seen #!/bin/bash at the
start of a lot of shell scripts.
It's wonderful because it allows me to have a uniform naming scheme for all my
scripts (no script_a.py,
script_b.js and
script_c.sh) or having to run them with
python script_a.py (and so on...).
Every script is called by its name as they would be any other commands.
#!/bin/bash
#!/opt/homebrew/bin/node
#!/usr/bin/env -S uv run --quiet --script
#!/usr/bin/env rustx
In one of our Python meetups, Tero gave an excellent lightning talk from which I learned how to build single-file executable Python scripts with dependencies. Combining that with the correct shebang and suddenly we have a Python script that takes care of its own dependencies and is still runnable just as a single command.
Now I can utilize whatever language I feel most comfortable in when solving a
specific problem. And as a user of my own scripts, I don't have to think about
that at all. I recently built a
,bookmarklet script in Javascript that
takes a filename as an argument, reads the file's contents and minifies the
code into a bookmarklet.
Lil' bit of custom tooling
One last thing I had to figure out was how to keep my scripts managed. They may live in different parts of the filesystem and they may live in folders with other scripts that I don't want to or need to manage.
So I created a repository called
pesky-little-scripts that has a single
shell script (link.sh) in the root and a
folder scripts/ where all my scripts
live in. That way, I can have all of them in one place and under version
control without having to do a lot of git excludes.
The link.sh script is my installer:
#!/bin/zsh
script=$1
scriptname=$(basename $script)
echo "Making $scriptname executable..."
chmod +x $script
echo "Linking $scriptname to /usr/local/bin...";
ln -s "$PWD/$script" "/usr/local/bin/$scriptname" # $PWD is needed since it requires an absolute path for the link to work.
rehash # Needed to update zsh cache for new scripts for autocomplete.
It makes the script executable (something I always used to forget), then
creates a symbolic link to
/usr/local/bin where my scripts live in
and finally, it runs rehash to update
the autocomplete cache so I can start to use the new script immediately.
Since it does not copy the file but links it, I can do all my future edits in this folder, keep track of its history and it will always be at its latest version when I run it.
If something above resonated with you, let's start a discussion about it! Email me at juhamattisantala@gmail.com and share your thoughts. This year, I want to have more deeper discussions with people from around the world and I'd love if you'd be part of that.