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 Substitution
Section titled Command SubstitutionIt took me a while to wrap my head around command substitution, but now it's a Bash feature I can't live without. 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
[1] - 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.[2]
Listing files from a commit
Section titled Listing files from a commitSo, 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[3] and ran with it, but I hate to miss an opportunity to recommend spending some time with the Git docs[4], 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 script
Section titled A simple Bash scriptNow 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 .bashrc
or .bash_profile
-- 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[5], 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 ~/scripts/edit_last_commit
.
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
OK. Let's roll this all into a bash script, with some annotation:
#!/bin/bash
set -e # this ensures the script will exit on errors,
# instead of doing things we don't want
if [[ -z "$1" ]]; then # if the first argument to the script is null,
commitish="HEAD" # default rev is HEAD
else
commitish="$1" # but if the first argument *does* exist, use it!
fi
vim $(git diff-tree --no-commit-id --name-only -r $commitish) # do the stuff!
Once the script is saved, give it the ol' chmod +x
[7] 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
[8] or that if
statement[9] 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!)
Conclusion
Section titled ConclusionI 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[10].
This could be shortened or further manipulated with history commands. Here's a good quick-reference for those on StackExchange: What are your favorite command line features or tricks? - Unix & Linux StackExchange ↩︎
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 $filename
makes $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 ↩︎