0

In the example at https://github.com/docker/docker-py, they return the results of a command to a docker image as so:

>>> client.containers.run("ubuntu:latest", "echo hello world")
'hello world\n'

What I want is to use a pipe - for instance, it would be great if I could do this:

>>> client.containers.run("ubuntu:latest", "echo hello world | wc")
'       1       2      12\n'

However, instead I get this:

 >>> client.containers.run("ubuntu:latest", "echo hello world | wc")
    b'hello world | wc\n'

What is the easiest way to run two commands, the second piped from the first, in docker?

lightning
  • 389
  • 1
  • 9

2 Answers2

3

Whenever you use a shell construct such as $ENV_VAR, |, etc, make sure you actually have a shell to interpret them or they'll have their literal values! To see why your invocation lacks a shell, you have to understand docker ENTRYPOINT and CMD.

If you look at the dockerfile for ubuntu:latest,you'll see that it is

FROM scratch

And the file doesn't set an ENTRYPOINT, only a CMD. Read What is the difference between CMD and ENTRYPOINT in a Dockerfile? for some good looking info about the difference. Suffice it to say that in your case everything after the image name replaces the cmd.

The doc for containers.run() says command can be a str or list. From that, and from observed behavior, we can infer the command string will be split on whitespace to create a list of arguments for the docker exec.

So, the answer, in short, is because | is a shell construct but you're executing no shell. There's a few ways to add a shell into the equation. The most obvious is to run the shell directly:

>>> client.containers.run("ubuntu:latest", "bash -c 'echo hello world | wc'",)
'      1       2      12\n'

But you could also set the entrypoint to a shell, which is commonly done in general purpose containers (though notice you still have to make sure that -c is provided, and the entire shell command must be enquoted as before. The entrypoint only provides the executable, not any arguments).

>>> client.containers.run("ubuntu:latest", "-c 'echo hello world | wc'", entrypoint="bash")
'      1       2      12\n'

The command line does the same thing with the standard input field separator:

$ docker run --rm -it ubuntu:latest echo hello world \| wc
hello world | wc

If we enquote the whole thing we defeat the automatic splitting around the input field separator:

$ docker run --rm -it ubuntu:latest "echo hello world \| wc"
docker: Error response from daemon: OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"echo hello world \\\\| wc\": executable file not found in $PATH": unknown.

The python equivalent is:

>>> client.containers.run("ubuntu:latest",["echo hello world \\|"])
Traceback (most recent call last):
  [... traceback omitted for brevity ...]
docker.errors.APIError: 500 Server Error: Internal Server Error ("OCI runtime create failed: container_linux.go:345: starting container process caused "exec: \"echo hello world \\\\|\": executable file not found in $PATH": unknown")
erik258
  • 14,701
  • 2
  • 25
  • 31
1

It is simply :

client.containers.run("ubuntu:latest", "sh -c 'echo hello world | wc'")
Dino
  • 7,779
  • 12
  • 46
  • 85
LinPy
  • 16,987
  • 4
  • 43
  • 57