Lazy Aliases & Bash Gibberish

I really do love aliases. I mean:

Off late I've been using docker a lot, and I find myself running these two commands often:

docker ps -a
To list all docker containers (running or stopped)
docker exec -it <container name> <cmd>
And to run a command on a container - most likely running bash to get shell access

Now that's a lot of characters - and I also really hate having to hit the spacebar - introduces even more scope for typos.

So I wanted to alias them to dps and dsh respectively.

The first one is straight forward. It can even just be a shell alias like alias dps='docker ps -a', but generally I prefer to keep them as tiny utilities in my ~/bin folder.

#!/bin/bash

docker ps -a
~/bin/dps

As for dsh, this one has some dynamic-ness to it. So here's the spec:

  1. Ability to specify the container name or ID
  2. Ability to specify command to run, but default to running bash since that is the most common use case.

Here's the 5-second solution:
(i.e. compromise by skipping half the spec)

#!/bin/bash

docker exec -it $1 $2

This works to some extent - the first argument is the tag, and the second is the command, but it doesn't gracefully fallback to using bash as the default. Also, this only works for one-word commands as arguments since we are using $2 and not $@ - but that is easily fixed even in the 5-second solution.

Now we could assign cmd=bash and then with check if $2 exists in an if condition and overwrite $cmd, but I wanted to find a one-liner akin to the logical OR operator that we have in JavaScript like const cmd = ARGV[1] || "bash" .

Enter Shell Parameter Expansion
(a.k.a the 20mins + write a blog post time solution)

Yeah that's a mouthful. As with most things to do with shell scripting - we're about to type a bunch of gibberish.

Turns out the shell equivalent of the JavaScript logical OR operator looks like this:

${parameter:-word}

If parameter is unset or null, the expansion of word is substituted. Otherwise, the value of parameter is substituted[1].

With some experimenting, I found that this works just fine:

#!/bin/bash

# First argument is always the tag
tag=$1

# throw away the first argument
shift

# Save the rest of the arguments as the command to execute,
# but if they don't exist assign string 'bash' to cmd
cmd=${@:-bash}

docker exec -it $tag $cmd
~/bin/dsh

I'm often using it like:

❯ dsh web
# => Enter bash shell at container web

❯ dsh alpine1 ash
# => Enter ash shell at container alpine1

❯ dsh alpine1 ping -c 1 google.com
PING google.com (172.217.166.174): 56 data bytes
64 bytes from 172.217.166.174: seq=0 ttl=37 time=11.380 ms

--- google.com ping statistics ---
1 packets transmitted, 1 packets received, 0% packet loss
round-trip min/avg/max = 11.380/11.380/11.380 ms

You can find my repository of tiny bin utilities over at github.com/vyder/bin


  1. Source: https://ss64.com/bash/syntax-expand.html#parameter