loading...
Cover image for Docker shell shortcuts - because writing full commands is hard!

Docker shell shortcuts - because writing full commands is hard!

tomgranot profile image Tom Granot Updated on ・5 min read

Containers for fun and profit!

So you're now officially excited about Docker and containers.

Good! They're an awesome tool to create portable configurations for your software and make sure it works seamlessly across multiple devices.

If you've been around them long enough, though, you're probably using Docker engine's CLI (command line interface) quite often.

Commands like:

docker exec -ti 1x81hs72k2s9 /bin/bash

or

docker start container-name

Are probably all over your shell history.

I had the same problem, and so I made a small hack and uploaded it to my slowly-expanding Useful Snippets repository (which you can totally star if you feel like it :) ):

GitHub logo tomgs / useful-snippets

Useful things I made/found over time

useful-snippets

In chronological order of additin, a bunch of useful stuff:

  • Base64-Encoded Flag Images (as JSON!) - link
  • Some shell snippets for better Dcoker - link

Money time

Specifically, if you go here you'll see the following snippets:

#!/bin/bash

# Get container id of existing container
dgrep(){
    docker ps -a | grep "$1" | cut -c1-12
}

# bash into an existing container
dbash() {
    docker exec -ti $(dgrep "$1") /bin/bash
}

# sh into an existing container
dsh() {
    docker exec -ti $(dgrep "$1") /bin/sh
}

# Execute something in an existing container
dex() {
    docker exec -ti $(dgrep "$1") $2
}

# Start an existing container
dstart(){
    docker start $(dgrep "$1")
}

# Stop an existing container
dstop(){
    docker stop $(dgrep "$1")
}

# Remove an existing container
drm(){
    docker rm $(dgrep "$1")
}

# Stop and remove an existing container
dsrm(){
    dstop $(dgrep "$1") && drm $(dgrep "$1")
}

# Remove an existing image
dirm(){
    docker image remove $(dgrep "$1")
}

# Get logs of an existing container
dlog(){
    docker logs $(dgrep "$1")
}

These go inside your shell's configuration file, probably located at ~/.bashrc if you're on one of the Linux distros and older macs, or ~/.zshrc if you're on newer macs.

After you put those commands inside your shell configuration file, whenever your shell starts up they will become available for use inside your session - just like regular shell commands.

Shell functions as command aliases

Note that what I did up there was define shell functions that perform specific commands, and take arguments just like regular shell commands.

This goes in contrast to defining shell aliases, like Nick Taylor does here (for the most part), and allows us to pass arguments to our "aliases".

This was a bit of a mouthful. Let's consider, as an example, the dgrep function I defined earlier:

# Get container id of existing container
dgrep() {
    docker ps -a | grep "$1" | cut -c1-12
}

dgrep() is a shell function that accepts an unlimited amount of arguments (as shell functions do). In order to access the arguments provided to the function inside the function's body, we use the $X notation, where X is an integer number.

$0 always refers to the name of the executing command, so if you're running:

my-command first-tom second-tom

And my-command is defined as

my-command() {
    echo $0;
    echo $1;
    echo $2;
}

Then the output will be:

my-command
1
2

Coming back to our dgrep command:

# Get container id of existing container
dgrep() {
    docker ps -a | grep "$1" | cut -c1-12
}

Note that what it's doing is getting the contents of docker ps (that is a list of all running containers), then piping the input into grep, which returns any match to the searched text with the provided argument.

In our case, it's "grepping" for $1, which is the first argument provided to dgrep. It finally pipes that output into cut -c1-12 that gets the first 12 characters of the piped text (which is, coincidentally, exactly the length of Docker container IDs).

For example, if you're running:

dgrep tom-container

Then if tom-container is a running container, docker ps will return a string containing all the information the Docker engine has about that container.

dgrep will then pipe it into the cut command, and print out only the container ID of that container.

But why do I need that?

Excellent question. Remember our example from the beginning of the article, where we used the full container ID to refer to the container?

docker exec -ti 1x81hs72k2s9 /bin/bash

Nobody expects you to remember the full names or the IDs of your containers. We can now re-write the command as:

docker exec -ti $(dgrep substring-in-container-name) /bin/bash

Where $() is a subshell - a special command that executes whatever is provided to it, and sends out the output as text before running the rest of the command.

In our case, $(dgrep substring-in-container-name) will find us a container ID based on some substring in the name (like my in my-very-long-and-hard-to-remember-container-name), and pass it to the docker exec -ti CONTAINER-ID /bin/bash command as CONTAINER-ID.

Wait, but that's still kinda long...

You're right! "Bashing" into a container (i.e. running bash inside that container interactively, which is basically "opening" a terminal to that container) is something we do quite often to debug long-running containers, and to do other fun filesystem shenanigans (ask me about that if you'd like to know more!).

In comes dbash():

# bash into an existing container
dbash() {
    docker exec -ti $(dgrep "$1") /bin/bash
}

As you can see, dbash is calling the previously mentioned dgrep in a subshell, and passes that subshell the argument provided to dbash - namely a substring of the relevant container.

This eventually lets us do something like:

dbash tom

And get a terminal to the first container on the list of running containers that has tom in its human-readable name.

Pretty neat, right?

Conclusion

Containers are fun. And so is working with the shell!

I literally just made a really nice hack using Docker for an open-source project I'm working on with my good friend, Federico. Feel free to take a look if you're into container wizardry!

Side note

An observant reader would note that I can save one pipe by using Docker command formatting. That reader would be correct, but I think my syntax is simpler and easier to explain to beginners, so I stuck with it for this tutorial (and, honestly, for my own ~/.zshrc as well).

Posted on by:

Discussion

markdown guide
 

Nice!

I had to make a minor change because I already have a function configured that redefines "docker ps -a" to sort and color results, which broke most of the functions. Switching the degree function to docker ps --all resolved the issue.

 

Do share! I kinda delegated coloring the shell to grc and starship, to be honest.

Oh-my-zsh is legit amazing, btw.

 
 

I personally dislike such approach. Bash complete + reverse search for often used commands makes its job well. So you don't need remember you shortcuts, which may be outdated, or just not available on remote machine.

 

Not having dbash on remotes have indeed bummed me out many times. One possible solution with machines that you manage yourself is having those shorthands backed up o git, then loading them in the machines that you manage like you do on your local one.

But that's not always possible, so what happens in practice is that I know most often-used commands by heart, and only use the shortcodes on my machine. So, different tastes I guess;)