Technical notes › Quickly create files from templates
An automation path I’ve seen few people reach for is having an easy ability to create a file from a template, automatically filling in its contents before opening it in your editor. I haven’t run the statistics on this, but probably something like 99% of the times I need to create a file in some folder, I already have a terminal open in that directory, so a command-line function is a nice and easy way to do it.
Since writing this function, I’ve started using it for all sorts of things:
Starting new projects with the configuration files I like. For example, when starting a Rust project, I’ll use my own
Cargo.toml
template rather than runningcargo init
, because my template includes the common dependencies, options, and profiles I’m going to be setting anyway.Initialising small playground scripts for when I want to test something. For example, I have a “Ruby + WEBrick” template that imports WEBrick and starts it listening on a port, so if I want to test something in isolation without all my existing code getting in the way, I can quickly spin up a new Web server.
A bunch of uncategorisable stuff such as “HTML5 page” which has the usual structure and common
<head>
tags in it.
My name is Newt
Because this creates new files from templates, I called my function newt
:
functions/newt.fish
function newt -d "Creates a new file from a template" -a template destination # check that all args are given if test -z "$template" echo "newt: Template argument not given" >&2 return 3 else if test -z "$destination" echo "newt: Destination argument not given" >&2 return 3 end # use template name if target is a directory if test -d "$destination" set destination "$destination/"(basename $template) end # check that target is available if test -e "$destination" echo "newt: ‘$destination’ already exists" >&2 return 1 end # check that the chosen template exists set -l tempeh "$HOME/.config/templates/$template" if test ! -f "$tempeh" echo "newt: No such template ‘$template’" >&2 return 2 end # apply the template and start editing it cp $tempeh $destination and eval $EDITOR $destination end
The script takes two arguments: template
, the relative path to the template file, and destination
, where it should put it.
It’ll then do a simple cp
to copy the template into the destination, and then open it in whichever editor you have $EDITOR
set to.
Shell completions
One of the great things about having all the templates as just files in a folder is that it’s dead simple to write a small shell completion function for newt
, letting you tab-complete the names of the templates:
completions/newt.fish
function __fish_list_newt_templates pushd $HOME/.config/templates fd -t f | sed -e 's,^\./,,' popd end complete -c newt -n __fish_is_first_arg -a "(__fish_list_newt_templates)" --no-files
These completions work by listing the templates directory using fd to list all files, skipping directories, and then stripping their prefix.
(There’s probably a better way to do this, but I failed to find it.)
We only want the first argument to be completed, so the completion is guarded with __fish_is_first_arg
.
Editor integration
You might find that your text editor already comes with a similar feature. If you’re lucky, it’ll use the same simple-tree-of-text-files set of templates used here, making it nice and easy to integrate the two together.
My primary text editor, BBEdit, calls this feature “Stationery” — that name being the very, very old Mac terminology for such a thing — and so I’ve simply symlinked ~/.config/templates
to the BBEdit Stationery folder, meaning I have access to the same set of templates from both within the application and from the command-line.∎