One of the things that helped me rediscover the joy of programming was making tiny shell scripts/functions for a specific purpose.
As I started using the command line more often, these functions helped execute commands that I would otherwise have to type repeatedly. And that set off a chain reaction - the more functions I made, the more ideas I'd get.
The first one I made was tzc
, to convert time from another timezone to my local time. It was inspired by two things:
- The RC application process, where all the interview timings were displayed in EST, and I needed a way to convert timings quickly, and
- CLI of the week #19, a post that introduced me to the idea of tiny shell functions.
Things I like about making tiny functions:
- I am the only user, so I make them in a way that makes sense to me (bonus points if it makes sense to others too).
- It takes less time to go from idea to execution.
- It's a great learning opportunity, as I end up learning something new about shell scripting concepts or the thing I'm making the script for.
I made a couple of functions while at RC - particularly over the last two weeks - which I'll share in this post.
note
I came across quicknote
recently, and really liked the way it was structured. It made it easier to capture notes and search them, so I made its zsh
equivalent:
#!/bin/zsh
note() (
dir=$HOME/notes
cd $dir
case $1 in
"open") shift ; micro $@ ;;
"ls") fd . $dir --type file | cut -d'/' -f5- ;;
"grep") rg $2 ;;
"rm") shift ; rm $@ ;;
*) _usage ; return ;;
esac
)
A neat trick I learnt while making this is writing the function block in regular braces ()
- which runs commands in a subshell as opposed to curly braces {}
- to prevent changing the directory of my current shell session.
The full code with error handling and usage information can be found here.
blog
This function is my take on a basic CLI content management system.
$ blog -h
blog - commands to manage your Jekyll blog
Usage:
blog command [args]
Options:
-h|--help Show this message and exit
Commands:
new filename Create a new post
update commitmsg Build and deploy changes to existing content
publish filename Publish post now
publish filename time Schedule post for a later time
preview View recent post/draft locally
preview all View entire website locally
These commands have existed as a bunch of separate shell scripts and aliases. Some of them have featured on this blog - a publish script and a post on scheduling.
The upgraded function code can be found here.
rlist
I use Safari's Reading List to save links, and I wanted a way to manage them from the command line, particularly to view all links and delete a link once I'm done reading.
$ rlist -h
rlist - Manage Safari's Reading List from the command line
Usage:
rlist [options] command
Options:
-h|--help Show this message and exit
Commands:
init Extracts links and saves to $HOME/notes/rlist.tsv
search Search all links and open chosen link in browser
delete Delete chosen link from list
The Reading List is stored in ~/Library/Safari/Bookmarks.plist
, and the function uses a utility called PlistBuddy
to interact with the file.
Here's a demo of the search command, that works like a fuzzy finder, along with the option to open the link in a browser.
This is possibly the coolest script I've made yet, as it took a while to understand how PlistBuddy
works, and using gum
for the fancy formatting added to the coolness.
mkfunc
Lastly, a meta function whose naming is inspired by mkdir
. It creates a file with starter code for a shell function when called.
#!/bin/zsh
mkfunc() {
if [[ ( $# -eq 0 ) || ( "$*" =~ "-h" ) || ("$*" =~ "--help") ]] ; then
echo "Usage: mkfunc name"
return
fi
tee $HOME/.oh-my-zsh/functions/$1 << 'EOF'
#!/bin/zsh
func() {
_usage() {
# Add help text here
}
if [[ ( $# -eq 0 ) || ( "$*" =~ "-h" ) || ( "$*" =~ "--help") ]] ; then
_usage
return
fi
# Remove this block if function contains no subcommands
case $1 in
# Add other cases here
*) echo "invalid option - $1" ; _usage ;;
esac
}
EOF
chmod u+x $1
$EDITOR $1
}
This function contains simpler help text, as there are no additional commands. The heredoc delimiter EOF
is wrapped with single quotes to prevent execution of the variables within the heredoc ($*
, $@
,$1
).
Thanks for tuning in to another episode of "things I didn't plan to work on"! It's interesting how I get more post ideas from side quests rather than the main ones 😅
You can find all the above scripts (and more, whenever I make them) at pjg11/scripts.