In this tutorial we will learn about the Docker command line and how to format output. This tutorial is language agnostic, applying to any containers you run.
Prepare the environment
To start, lets run a container to make sure docker is up and we have something to query later:
docker container run -d -p 80:80 nginx
This lab requires jq
which is installed in the play-with-docker machines for you. If you run this on another machine, you may need to first install the jq
package.
The help flag
To understand the docker command line, we can use the --help
option for more details. Lets start at the top level:
docker --help
Scrolling back, you should see the following:
Management Commands:
app* Docker Application (Docker Inc., v0.8.0)
builder Manage builds
buildx* Build with BuildKit (Docker Inc., v0.3.1-tp-docker)
config Manage Docker configs
container Manage containers
context Manage contexts
engine Manage the docker engine
image Manage images
manifest Manage Docker image manifests and manifest lists
network Manage networks
node Manage Swarm nodes
plugin Manage plugins
secret Manage Docker secrets
service Manage services
stack Manage Docker stacks
swarm Manage Swarm
system Manage Docker
trust Manage trust on Docker images
volume Manage volumes
Docker has organized commands into a docker <noun> <verb>
syntax with the above nouns. There are also aliases to many commands at the top level. For example, docker run
is the same as docker container run
. To see the verbs available to docker container
, we can run:
docker container --help
From there we see the ls
command. Examine the help output for that to see how you would show all containers, not just running ones:
docker container ls --help
Solution
docker container ls -a
Simple Formatting
Next, lets look at a different command:
docker system info
That’s a lot of output. It would be nice to be more selective in what we show. First, check the help output:
docker system info --help
We’re in luck, there’s a format flag. If you’ve never used this, it uses the Go Template syntax. In this syntax, we can type any raw string to simply output it, and then instructions to be parsed are included within {{ }}
. Variables injected into our template are prefixed with a .
. To get just the kernel version, we’d use a command like the following:
docker system info --format 'The kernel version is: {{.Kernel}}'
But unfortunately, that didn’t work. We need to first figure out what the field name is to input. My favorite method for that is to convert the available fields and values into json using:
docker system info --format '{{json .}}'
Oh no, that looks horrible. Lets run that through jq
to make it look pretty:
docker system info --format '{{json .}}' | jq .
That’s more like it. Scrolling back up we can see the field we were looking for, KernelVersion
. Try outputting only that field.
Solution
docker system info --format 'The kernel version is: {{.KernelVersion}}'
Table Formatting
The above formatting is useful when we are showing a single thing, but what about commands with a list of output like docker container ls
. How can we alter that output and keep the columns? First, lets pick what columns we want to display:
docker container ls -a --format '{{json .}}' | jq .
We can display just the ID and image with:
docker container ls -a --format '{{.ID}}: {{.Image}}'
If we want to display more columns, and with the table layout, we can use the table
syntax with \t
between each column:
docker container ls -a --format 'table {{.ID}}\t{{.Image}}\t{{.Command}}\t{{.Status}}'
Try it yourself with other commands. Start with the container stats:
docker container stats --no-stream
How would you format that to only show the CPU, Memory, Network IO, and Name, maintaining the table format?
Solution
docker container stats --no-stream --format 'table {{.CPUPerc}}\t{{.MemPerc}}\t{{.NetIO}}\t{{.Name}}'
Advanced Formatting
There’s a lot more you can do with formatting. Lets take a look at the environment variable list for the last run container (you didn’t skip the setup step did you?):
docker container inspect --format '{{json .Config.Env}}' $(docker container ls -lq) | jq .
So we have a list of strings, which jq
at least shows as a nice list. If we wanted to parse those lines, we could run this long line (if you are typing this by hand, this is one line, ignore the line wrapping):
docker container inspect --format '{{range .Config.Env}}{{with split . "="}}{{printf "%s: %s\n" ( index . 0 ) ( index . 1 )}}{{end}}{{end}}' $(docker container ls -lq)
Wait… what?? That got complicated really fast. Lets break that down:
range
: This allows you to iterate over an array, setting the.
value to each array element.with split
: Thewith
command runs the nested statement with a new value for.
. And thesplit
command parses each line, separating it into an array using the=
as a delimiter.printf
: This formats some output, useful when you want to truncate a string, or in this case include linefeeds.index:
This selects an element from an array. We’re in luck that Go starts their arrays at 0 like a proper language.end:
These close therange
andwith
loops.$( ):
This is bash shell notation to run a command and include the resulting output in current command. Try running this command by itself, and check the help ondocker container ls
to see what those options do.
We can get even more complicated. How could we print each of the elements of the PATH variable on separate lines? We need two additional parts of templating, the conditional and the condition being evaluated.
{{ if ... }} ... {{end}}
The if
conditional will run the included section when the condition is true. There are also the {{ else if }}
and {{ else }}
options to an if clause. And of the condition itself we can use eq
:
{{ if eq .variable "value" }} True template {{ else }} False template {{ end }}
Using this, and the other template operators above, try creating a command that will print the PATH variable elements on separate lines.
Solution
Note, this is one long line (if you are typing this by hand, this is one line, ignore the line wrapping):
docker container inspect --format '{{range .Config.Env}}{{with split . "="}}{{if eq (index . 0) "PATH"}}{{range ( split (index . 1) ":" ) }}{{printf "%s\n" .}}{{end}}{{end}}{{end}}{{end}}' $(docker container ls -lq)
Further Reading
If you’d like to learn more, checkout these resources:
- Go Template Syntax
- Docker Formatting Commands: these commands are in addition to those provided by Go
Quiz
How can we see all possible variables for a format? Select only one option
- (x)
--format '{{ json . }}'
- ( )
--format '{{ printf . }}'
- ( )
--format '{{ .List }}'
What does the index
template function do? Select only one option
- ( ) Iterates over all elements in a list
- ( ) Provides faster lookups over keys in a table
- (x) Selects an item from an array
How do we get usage information for a docker command? Select only one option
- (x)
--help
- ( )
-h
- ( )
/?