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: The with command runs the nested statement with a new value for .. And the split 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 the range and with 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 on docker 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:

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
  • ( ) /?