Effective Bash Shell Usage

Introduction

I spend a lot of time using UNIX shells interactively and so I care a little bit about my shell environment. I've previously documented how I configure my terminals, but this is more about how I use them. Because it makes me die inside to watch someone else using their shell and wasting time or not using the best tools.

Additional Tools I Always Install

Exa

This is a better ls than ls. It does use a different format than ls and so if you wrote scripts that parse the output of ls, they're probably going to break if you alias exa to run whenever you type ls - but you're also doing it wrong if you parse the output of ls (hint: there's almost always a better way to do what you're doing when you feel the urge to parse the output of ls).

The only thing I find silly about this tool is that there isn't a column showing the group when printing the long output of files. That's easy enough to fix with an alias though (which is exactly what I do). Why isn't this in the default output though?

Bat

This is a better cat than cat. It recognizes and highlights syntax for a ton of common file formats, prints line numbers, and automatically invokes a pager when viewing larage files. Definitely not compatible with /bin/cat but it's easy enough to get that behavior either by explicitly calling /bin/cat or through the use of options.

I use it enough that I alias it to get called in place of the system's cat executable.

Mdcat

Markdown has quietly taken the world by storm, and it's nice to be able to get a quick rendering of a .md file without having to leave my terminal.

Jq

Thankfully much of the world has moved on from using XML to JSON. Jq is great for not only formatting JSON, but also for chewing it up, and it's reasonably easy to iterate over a command a few times to get just the bits you want out of it.

Gron

Gron flattens JSON which then makes it grep'able. This is invaluable for exploring what would otherwise be large blobs of under-documented JSON.

My Aliases

Insert .bash_aliases here


# if we don't have vim, don't break vi
if which vim > /dev/null ; then
    alias vi="vim"
fi

# if we don't have bat, don't break cat
if which bat > /dev/null; then
    alias cat="bat"
    # removes line numbers which makes it suitable for copying
    alias catp="bat -p"
fi

# if we don't have exa, don't break ls
# specify -g to print group info when using a long form
if which exa > /dev/null; then
    alias ls="exa -g"
fi

# these are good whether we have exa or not
alias ll="ls -l"
alias la="ls -a"
alias lla="ls -la"

alias open="xdg-open"

# preserve ansi color codes between jq and less
alias less="less -R"
alias jq="jq -C"

I conditionally source this in my .bashrc file. It's handy to keep it as a separate file so that I can make changes to it and source it manually without reloading everything which tends to cause chaos with things like $PATH since it's generally built additively.

Keyboard Shortcuts for Command Line Argument Reuse

!$

Expands to the last argument of the previous command. I use this all the time because it's a super common pattern that a few commands in a row operate upon the same file and it comes last in the command. A good example is redirecting stdout from a command to a file - and then renaming it, and then opening it with an editor. That becomes something like:

somecommand > foo.out mv !$ bar.out vi !$

!!

Expands to the entire previous command. This was made critical when it became fashionable to use sudo to run commands as the super user rather than keeping a root shell open... the pattern is to run a command that needs root without realizing it, getting some sort of error that tells you it should have been run as root, and then typing sudo !! to rerun that command but with sudo. I use this at least once or twice a day probably.

!*

Expands to all of the arguments supplied to the last command - so if you run a command with a handful of arguments and wish to run a different command but with the same arguments, this is super handy. I tend to use this with aliases that add on extra flags to things that I frequently.

Bash offers a ton of shortcuts for additional stuff, but I've found that there is a long tail on their usefulness and the above gets me most of what I want without having to remember all of the shortcuts.

Explicitly End Arguments with --

This one's really simple, but too many people that use UNIX don't know it. The GNU core utilities have made us all a bit sloppy because they're really forgiving on what they'll accept - but you're supposed to include all command line flags and switches before any variable numbers of file arguments at the end of the command. If you need to express the end of the positional arguments and the beginning of the file arguments, you can usually add a double-dash (--) argument which the argument parser will interpret as "we're done parsing switches and flags and everything from here on out is a fiename". In shell scripts, you really want to use this if you're running commands based on user input to help provide unexpected behaviors, but it's also useful when running interactively. Files that begin with dashes can be otherwise problematic to work with.