Docker Community Forums

Share and learn in the Docker community.

Format the output of "docker ps" or "docker container ls" using the slice function in go template

I have a strange result with docker container ls --format when I want to shorten the image id or name. I will continue to investigate the issue but I think it is interesting enough to share.

Start a PHP container

docker run -d --name php php:8.0-apache

Note: It doesn’t matter which container I start. I chose this because the name of the image is long enough. You will see.

Show the first 11 characters of the images of the running containers:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ slice .Image 0 11 }}

Note: I used --filter to make sure the error message is not caused by another image

The result:

failed to execute template: template: :1:3: executing "" at <slice .Image 0 11>: error calling slice: index out of range: 1

However, when I want to get the first 10 characters it works:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ slice .Image 0 10 }}

The result:

php:8.0-ap

So i thought I used slice incorrectly but slice works with a static string:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ slice "php:8.0-apache" 0 11 }}'

Then I thought .Image was actually an object so that was why I coudn’t manipulate it as a string. So I found this:

It is about the last characters not the first but it was the closest I found.

“pyhedgehog” wrote this:

.Image field is of some strange type (not string), so you should evaluate it to string (using print )

But using print doesn’t change anything:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ $i:=(print .Image) }}{{ slice $i 0 11 }}'

Result:

failed to execute template: template: :1:27: executing "" at <slice $i 0 11>: error calling slice: index out of range: 11

But… in the template string on stackoverflow there is an “if” statement. So I tried to play with that:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ if ge (.Image|len) 11 }}{{ slice .Image 0 11 }}{{end}}'

Note: I checked if the length of the image name was greater then 11 which always true for this image. It worked:

php:8.0-apa

If I try it with 10 iin the condition nstead of 11, it failes again:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ if ge (.Image|len) 10 }}{{ slice .Image 0 11 }}{{end}}'

Result:

failed to execute template: template: :1:30: executing "" at <slice .Image 0 11>: error calling slice: index out of range: 11

It seems the implicit conversion happens when I check the length of the name. The number in the condition must be greater then 10 to be able to slice more than 10 characters.

I also tried this:

docker container ls --all --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ slice (printf "%s" .Image ) 0 11 }}'

Failed again.
I couldn’t explain it so I tried to use slice with docker image ls --format. Surprisingly it worked without errors.

docker image ls --filter "reference=php:8.0-apache" --format '{{slice (printf "%s:%s" .Repository .Tag) 0 11}}'

I could use the printf function to get the first characters of the name, but I also want to set an offset since I am working on a demonstration in which I have Image IDs starting with sha256: which I also want to cut out and I couldn’t do it with printf. This last sentence is just an explanation why I am playing with slice, not a question.

Can anyone explain it? I could use the ugly workaround with the condition but that looks bad in a demonstration.

1 Like

Very interesting research.

This one smells strongly like a bug in container ls.

Is it possible that the default slice “barrier” has a capacity of 10 and that wrapping it in a length comparision accidently triggered a higher capacity? Setting the upper bondary to (len .Image) has the same effect:

docker container ls --no-trunc --filter "ancestor=php:8.0-apache" --format '{{ slice .Image 0 (len .Image) }}'

I don’t think this is related to type of .Image itself, because the (printf "%s" .Image) definitly is a string, and the slice still hits the capacity barrier on it.

If you want, I have an even ugglier workaround :smiley:

docker container ls --no-trunc --filter "ancestor=php:8.0-apache" --format '{{json .}}' | jq -r '.Image | .[0:10]'

I like jq and I thought of it as a solution but I try to avoid additional tools since I don’t want to force people who read my tutorial to install more packages if there is another way.

I also want to format the output as a “table” which is available in the format string. I could use column to achieve something similar but that would complicate the final command more. When I create a tutorial I try to keep it as simple as it is possible.

Everything is possible at this point :slight_smile: But why would it work with static string? I haven’t read the source code yet and I am not mentally ready to do it today.

Thanks. I didn’t know that. Unfortunately it won’t help but maybe I can figure something out. If not, that’s OK. I can mention this issue and use a more complex solution.

Thanks for the suggestion.

Good question…

I don’t understand why those two are not producing the same result:

docker container ls --filter "ancestor=php:8.0-apache" --format '{{ $i:="php:8.0-apache"}}{{slice $i 0 12}}'
docker container ls --filter "ancestor=php:8.0-apache" --format '{{ $i:=(printf "%s" .Image)}}{{slice $i 0 12}}'

While the first variation works, the second does not. Even though I would expect printf "%s" .Image to create a new string, identical to the static string, but it is treated differently.

I hope you find the root cause, or at least a statisfying understanding what causes it :slight_smile:

This works too

docker container ls --filter "ancestor=php:8.0-apache" --format '{{ $i:=(printf "%s" "php:8.0-apache")}}{{slice $i 0 12}}'

So “printf” in parentheses doesn’t create an object. I wasn’t sure until now.