My "at" scripts

There was an interesting discussion on HN about personal scripts. Lots of fun tidbits! My comment about my “at scripts” was quite well received, and people seem to find this useful every time I bring it up, so I figured the topic probably deserved its own blog post.

At what?

“At scripts” (@scripts) are what I call the rather large and motley collection of scripts that I’ve amassed over nearly 30 years. The one thing they all have in common, is that they prepare or modify my environment in some way. Whether it’s changing my shell environment for a specific project, my physical environment by dimming lights, or my networking configuration based on where I am.

This sort of started in the late 90s when I first started running BSD and Linux. I had a friend and an uncle who were both way more knowledgeable and who both happened to leave nearby. I would lug my PC to their place for shits and giggles. I quickly found it annoying to have to remember how to connect to their networks, so I wrote two scripts: @uncle, and @friend. There wasn’t anything special to those scripts, it was just ifconfig and probably some host file fiddling. But it was convenient, and the idea stuck.

Those types of networking-environment scripts became more important when I got my first laptop and became a bit more mobile (if you can call lugging a brick shithouse around “mobile”). I remember I had several in college, including @rooftop for “unofficial” wifi access when I was busy cutting classes on the roof of the building, and @lab for the computer labs.

Projects

Where these types of scripts really began to shine over the years, is the many projects I’ve worked on. Customer projects, FOSS projects, personal projects, you know how those things go. I’ve had so many of them. Sometimes multiple at the same time. Sometimes I have to come back to a project after several years. I can’t be bothered to remember how to set up an environment for a specific project. I also can’t be bothered to remember many of the common tasks in a project (e.g. building it, running tests, creating a release, whatever).

Here’s the output of running the @pulsescript, which is a project for a customer I’m currently working on:

➜ ~ @pulse
 ____        _
|  _ \ _   _| |___  ___ 
| |_) | | | | / __|/ _ \
|  __/| |_| | \__ \  __/
|_|    \__,_|_|___/\___|

Using java version 21.0.7-tem in this shell.
Using maven version 3.9.9 in this shell.
Now using node v24.0.1 (npm v11.3.0)

vpnon: enable Pulse VPN
vpnoff: disable Pulse VPN
tests: run all tests
build: build the webapp
containers: build all docker containers

Aside from preparing the environment and dropping me in the correct directory, it also creates a bunch of shell aliases and tells me what they do. This has saved me untold amounts of time over the years. It takes 20 minutes or so to create the scripts and automate everything, and it requires minimal maintenance over a multi year period, e.g. when switching to a newer Java or Maven version.

When I create one of these scripts, especially if it’s for a customer project, I always take some extra time to ensure that nothing can bleed over from one project to another. This means creating different SSH keys for their git repositories, configuring maven to store its dependency files in a project-specific cache instead of a global cache, to use a customer-specific ansible inventory. Some projects run entirely in containers, others don’t. But the trusty @script will always hide that complexity by creating aliases that include tedious command line flags, so I can focus on the project proper instead of its environment.

The script itself looks like this, and there’s a simple shell alias @pulse which sources the script.

figlet Pulse

sdk use java 21.0.7-tem
sdk use maven 3.9.9
nvm use 24.0.1

alias mvn='mvn --settings ~/projects/Pulse/.tools/maven/settings.xml'
alias vpnon='~/projects/Pulse/.scripts/vpnon.sh'
alias vpnoff='~/projects/Pulse/.scripts/vpnoff.sh'
alias tests='~/projects/Pulse/.scripts/tests.sh'
alias build='~/projects/Pulse/.scripts/build.sh'
alias containers='~/projects/Pulse/.scripts/containers.sh'

~/projects/Pulse/.scripts/help.sh
cd ~/projects/Pulse

Meatspace

I’ve also used various scripts for automating things in meatspace. @night to dim the lights, @gf for creating a more romantic ambiance. There’s really no limit to their usefulness. And I get it, they’re just scripts, there’s nothing special about them. It’s just that over the years the @-prefix stuck because it doesn’t conflict with anything, and occasionally people would see me use one of those and think it was cool.

Alternatives

There are many alternative ways of doing similar things. Some of them (like direnv) rely on magical .envrc files, and magic is something I really dislike in my shell. Another one which HN commenters recommended is mise-en-place (or mise for short). Other ecosystem-specific tools exist as well, such as sdkman for the broad Java ecosystem. There’s nothing stopping anyone from using these tools or from combining them with hand-rolled scripts. Many of my @project scripts use sdkman to configure the Java tools.

Next time you find yourself taking on a new project, why not automate some of this stuff using an @script of your own?

— Elric