Error executing command with docker run

Hello!

I am having an issue executing this command docker run alpine cat /etc/*release* on my macOS 10.13.1.

I get a response of zsh: no matches found: /etc/*release*

When I use online playgrounds like katacoda to try the command, it works fine.

What could be wrong on my side please?

any help please? I have done so much googling to no success.

When you run that command, zsh tries to expand /etc/*release* itself instead of passing it to the command to be executed in docker. So it actually tries to look up /etc/*release* in your local MacOS installation and fails to find it, leading to that error message.
What you should actually do is run:

docker run alpine /bin/sh -c "cat /etc/*release*"
2 Likes

Thank you Johan.

This explains it, however, alpine uses ash for its bash so the updated command that worked is

docker run alpine /bin/ash -c “cat /etc/*release*”

No, it doesn’t :wink:

% docker run -it --rm alpine
/ # ls -l /bin/sh /bin/ash
lrwxrwxrwx    1 root     root            12 Jan 16 21:52 /bin/ash -> /bin/busybox
lrwxrwxrwx    1 root     root            12 Jan 16 21:52 /bin/sh -> /bin/busybox

Glad I could be of help.

I actually arrived here after googling the issue. Thanks. I’m trying to make sense of this command by checking the documentation. It’s not the easiest to make sense of. Any chance you can explain what the command is actually doing?

Thanks either way!

I’m not sure if it will answer your question but let me share something with you: from the below, I can conclude that even if the cat command is executed in a contained the local presence of the same filename and path is required. It probably is a MacOS-only issue.

sh-3.2# docker run ubuntu cat /etc/*release* 
cat: '/etc/*release*': No such file or directory
sh-3.2# echo "Fake release" > /etc/os-release 
sh-3.2# docker run ubuntu cat /etc/*release* 
PRETTY_NAME="Ubuntu 22.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="22.04"
VERSION="22.04.1 LTS (Jammy Jellyfish)"
VERSION_CODENAME=jammy
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=jammy

It is not just a macOS issue. You replied to an old question so I don’t think @abdulwahabadi is still waiting for the answer, but for anyone else, everything after the name of the image in a docker run command is an argument of the entrypoint defined for that image. ENTRYPOINT + aguments will be the final command. The ENTRYPOINT is optional and the base alpine image doesn’t have it, so let’s forget about that for a moment.

The terminal where you execute a command on your host machine will be the first to interpret the command. Any special character that is not escaped (backslash) or surrounded by quotation marks or sometimes apostrophes will be interpreted by your host shell, not the container.

docker run --rm -it alpine echo $HOME

for example will echo the home of the user on the host machine, because the host shell interprets the variable and executes:

docker run --rm -it alpine echo /Users/username

The dollar sign is not the only special character. The * character can be used as a wildcard character, and this is what happened here. If you run the following commands:

docke run --name wildcardtest alpine cat /etc/*release*
docker ps -a --no-trunc --filter name=funny_sinoussi --format '{{ .Command }}'

you will see that the container is failing on macOS with this command:

cat /etc/*release*

This is the entire command because the wildcard character was not interpreted by the host shell and there is no shell involved in the execution of cat in the container to understand *.

Now what about Linux? On Linux /etc/os-release usually exists and on many systems ther eis even /etc/lsb-release`. In that case the cat command would be interpreted on the host and the container actualyl runs

cat /etc/lsb-release /etc/os-release 

Now lsb-release is not something that you would find in most of the containers, so this command would partially fail. It would show warn about the missing lsb-release and show the content of the os-release file. Even os-release is not guaranteed to be in a container. So what if you are runing the following command on Linux:

docker run -it busybox cat /etc/*release*

It would fail, since busybox doesn’t have any release file, but the host had one or two so you could get this:

cat: can't open '/etc/lsb-release': No such file or directory
cat: can't open '/etc/os-release': No such file or directory

So the right solution is exactly what @johanmulder suggested. And what happens is that by starting the command with /bin/sh you are executing a shell. You could just pass the path of the shell and run the rest of the command interactively, but you can also use the -c option of the shell which expects a string which will be executed by the shell itself, which understands the wildcard characters. This is why the following command with quoating only the argument of cat is not enough

docker run --rm -it alpine cat "/etc/*release*"

In this case the host shell could not interpret the wildcard character since it is quoated, but you still don’t have a shell in the container.

So it actually tries to look up /etc/release in your local MacOS installation and fails to find it, leading to that error message.

No. Wrong. You can put the * in quotes and it won’t interpret it locally.

eg

docker exec ff2 ls "/etc/*" 
ls: /etc/*: No such file or directory

The container actually receives the * perfectly fine.

Don’t believe me? Try this:

docker exec ff2 touch "/etc/*"
docker exec ff2  ls -l "/etc/*" 
-rw-r--r--    1 root     root             0 Oct 16 13:52 /etc/*

The problem is that you are “exec” ing the specified command in the container. You are not running a shell in the container to run the command. The SHELL is the bit that converts * or other wildcards/special characters into what you expect.

The fix is the same regardless /bin/sh -c "command"

I’m not wrong. Read and interpret my answer again:

I know this is a revived dead thread, but do you know why is it that cat fails to find matching files when not specifying the /bin/sh -c part in your command?

Not the best way to describe a solution as there is a chance that you just misunderstood something as you probably did :slight_smile: The post you replied to already stated the same. Quoting just a sentence from a post could be misleading, but thank you for sharing your thoughts.

1 Like

It has nothing to do with cat. It is shell expansion. Without running a shell with the command as argument between quotation marks you use the host shell and the command will be sent to the container only after the shell on the host interpreted it. And you need to define a shell, because just using quotation marks makes the defined command to be a reference to a single executable. Which will not exist.

Ah, I see, passing it without /bin/sh does not expand the *s
It does not however handle it as a single argument, simply doesn’t find the correct files


Sorry, my mistake, what I wrote above applies to docker run alpine cat "/etc/*release*", not "cat /etc/*release*"
That’s when /bin/sh -c allows the container’s shell to expand the *

Looks like I mixed up the reply with someone suggesting that when you quote the arguments, they’re interpreted on the host. Even with proper quoting, nothing happens on the host, but the command still fails in the container.

I am afraid the test does not prove what you think it does. It only proves that a file name can include a star character. You literally use a static string in your test. This topic is about file globing (aka file name extrapolation), which your example does not use at all.

@rimelek’s response is spot on!