Runar Ovesen Hjerpbakk

Programmer. Software Architect. Technical Manager.

Docker, bash and invisible characters

Consider the following Dockerfile, perhaps the simplest in existence, which echoes back the argument given to it:



And this small script,, which builds the container, runs it with the argument 42 and verifies that the output is the same as the input.

#!/usr/bin/env bash
docker build -t hjerpbakk/example .

RESULT=$(docker run --rm -it hjerpbakk/example "$ARGUMENT")

if [ "$RESULT" = "$ARGUMENT" ]; then
   echo "container ran successfully";

echo "container failed. Expected:"
echo "$ARGUMENT"
echo "but got:"
echo "$RESULT"
exit 1;

Surprisingly, 42 is not equal to 42. What is going on here?

Docker output gotcha

After a little bit of DuckDuckGo-ing, it turns out Docker sometimes adds a carriage return at the end of the output. To remove a superfluous carriage return, the tr command can be used.

Thus the script can be changed to pipe the result through tr, removing any \r in the output:

RESULT=$(docker run --rm -it hjerpbakk/example "$ARGUMENT" | tr -d '\r')

It feels hacky, but the script completed successfully.

Digging deeper

In the docker-command above I’ve used -it:

  • -i is short for –interactive. Keep STDIN open even if unattached.
  • -t is short for –tty. Allocates a pseudo-terminal that connects your terminal with the container’s STDIN and STDOUT.

Some more research revealed that -t is responsible for the extra \r. For my use, neither -i or -t are needed. Removing -it also removes the need for the hack, making the final script:

RESULT=$(docker run --rm hjerpbakk/example "$ARGUMENT")

As Dave Agans says in the book Debugging: The 9 Indispensable Rules for Finding Even the Most Elusive Software and Hardware Problems, the first order of business is to see the system fail. Only then can you can analyze how it fails and finally fixed the problem when you know why it fails.

If you don’t understand why you fixed the problem, as in the tr hack above, you haven’t actually fixed it.