Over the years I've greatly enjoyed learning how to simplify common actions in the terminal. I want to share a quick, simple example of a handy little helper-script, and encourage you to add these kinds of helpers to your workflow. It's a great way to improve or maintain your Bash skills, and anyway making a task easier or faster is probably the foundational joy of programming.
I found myself in a situation last week where I wanted to quickly reopen all the files from my last Git commit. It's easy to do this, but in my experience one of the difficulties of Bash is knowing what keywords to search for to find help, so I'm going to put the pieces of this together one by one, with some other helpful tricks, so that you don't have to wait as long to find each piece as I did.
Command SubstitutionSection titled Command Substitution
One feature of Bash that I took far too long to fully discover and understand, but now can't live without, is command substitution. In short, it's a shortcut to simplify using the output of evaluating a command as input to some other command. This is possible with pipes, but often more verbose. The short form is simple --
$(command). For example, one might be looking around for something in a project dir with
rg 'function_name' - and then decide it's time to edit.
rg lists matching files with the
-l switch, so you can invoke
vim $(rg -l 'function_name') to open all the search results as vim buffers. This could be shortened or further manipulated with history commands. I've found a good quick-reference for those on StackExchange; do yourself a favor and give it a look if you aren't familiar, but I won't go into detail here as we're already getting a bit afield.
Listing files from a commitSection titled Listing files from a commit
So, if we have a command that can produce a list of files from the last commit with no other data, we know how to easily bring them into vim. Luckily we have options! When I set this up I definitely just found the relevant StackOverflow and ran with it, but I hate to miss an opportunity to recommend spending some time with the Git docs, where with a little determination and trial-and-error you could find your way to the right answer as well. The recommended answer that we'll use here is:
git diff-tree --no-commit-id --name-only -r <commit-ish>
A simple Bash scriptSection titled A simple Bash script
Now it's almost as simple as combining those two pieces! Yes,
vim $(git diff-tree --no-commit-id --name-only -r HEAD) works, but it's verbose and a bit tricky to remember. We could fix this with an alias in
alias edit_last_commit=vim $(git diff-tree --no-commit-id --name-only -r HEAD) -- cool! BUT, what if we want to pull up some other set of changes? I'm not sure if this is possible with an alias, but it definitely is with a little script.
I don't know if there's a more recommended or standard way to organize scripts like this[5:1], but my approach is to make a dir at
~/scripts and make sure it's on my
$PATH. So, we'll put this little buddy at
The only other detail to comment on here is one I've elided so far: how to refer to whatever set of changed files we want to edit. What I'm getting at is, what is a
That's a lot of bits and bobs! I hope at least one of them is new and useful to you. Let's put them all into a bash script, with some annotation:
#!/bin/bash # shebang line to ensure we end up evaluating this as bash!
set -e # only here out of habit; this ensures the script will exit on errors rather than causing mayhem (unexpected behavior)
if [[ -z "$1" ]]; then # if the first argument to the script is null (in other words, called with no args)
commitish="HEAD" # default rev is HEAD
commitish="$1" # but if the first argument *does* exist, use it!
vim $(git diff-tree --no-commit-id --name-only -r $commitish)
Once the script is saved, give it the ol'
chmod +x and you're all set.
I'll say it again: Bash has some damn near impenetrable and difficult-to-search syntax. Please do take a look at documentation for
set -e or that
if statement to learn more; these are bread-and-butter for small helpful scripts such as this one so it's worth understanding them and related options (
set -x can be very handy too!)
ConclusionSection titled Conclusion
I have called this script "simple" but it's only simple to me because I've spent years muddling about and haphazardly learning about these components along the way. If this is helpful to you, I'm so glad and I hope you can do more, faster for not having to piece all of this together yourself. If you've been muddling longer than me and have suggestions for improvements? I'd love to hear about them, within reason.
I find that I'm constantly re-learning the value of spending some time reading documentation, manual pages, etc. for commonly used tools - for me, recently this means git, tmux, and vim - so, here's the Git Book section on viewing commit history, and I strongly recommend taking some time now and then to read up on the amazing tools at your disposal! ↩︎
chmod +x $filenamemakes $filename executable for all users! probably fine on your own computer, but better to be more specific with permissions on other systems, to maintain a habit of care around possible security issues. ↩︎
please don't tell me I ought to use emacs or zsh, for example ↩︎