Docker image gives me "executable file not found in $PATH" when run from python
<p>I'm trying to run some commands inside a docker image in python. When I do:</p>

<pre><code>docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim sh -c "javac example.java &amp;&amp; java example"
</code></pre>

<p>In console (kali linux) it runs fine and prints the result. When I try to run same command from python it gives me the error:</p>

<pre><code>"Exception: docker: Error response from daemon: oci runtime error: exec: \"sh -c 'javac example.java &amp;&amp; java example'\": executable file not found in $PATH.\n"
</code></pre>

<p>These lines work fine from python:</p>

<pre><code>docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim sh
docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim javac
docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim javac example.java
</code></pre>

<p>But these don't:</p>

<pre><code>docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim sh javac
docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim sh javac example.java
docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim sh -c javac
docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim sh -c "javac"
</code></pre>

<p>They all work from console. Just not from python. As soon as I add "sh -c" it gives me the error. I can run it as 2 separate commands like:</p>

<pre><code>docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim javac example.java
docker run --rm -v &lt;some_dir&gt;:/mnt --workdir /mnt frolvlad/alpine-oraclejdk8:slim java example
</code></pre>

<p>But it's important that it runs all at once.</p>

<p>It all works fine from console, but in python as soon as I add "sh -c" it gives me the error. What am I doing wrong?</p>

<p>Here is the python code that I use:</p>

<pre><code>BASE_CMD = [
    'docker',
    'run',
    '--rm',
    '-v'
]

def calculate_compile_and_execute_java_command(folder_name, file_name):
    return BASE_CMD + [
        folder_name + ":/mnt",
        "--workdir",
        "/mnt",
        "frolvlad/alpine-oraclejdk8:slim",
        "sh -c 'javac " + file_name + " &amp;&amp; java " + file_name[:-5] + "'"
    ]
.
.
.
response = call_command(calculate_compile_and_execute_command(self.lang, file_path, file_name))
.
.
.
def call_command(cmd, timeout=float('inf'), cwd=None, decode=True, **subproc_options):
    if cwd is None:
        cwd = os.getcwd()

    subproc = subprocess.Popen(
        cmd,
        cwd=cwd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
        **subproc_options
    )
    .
    .
    # code for timeout handling and result polling
    .
    .
</code></pre>

<p>I tried all possible combinations of string concatenation and quotes escaping in calculate method. Popen() sends the string to terminal as it should. It just doesn't work with "sh -c". It even works if I add more switches to the command. Like this:</p>

<pre><code>def calculate_compile_and_execute_c_command(folder_name, file_name):
    return BASE_CMD + [
        folder_name + ':/mnt',
        'frolvlad/alpine-gcc',
        'gcc',
        '--static',
        '/mnt/' + file_name,
        '-o',
        '/mnt/qq'
    ]
</code></pre>

<p>Which gives the string like this:</p>

<pre><code>docker run --rm -v "$(pwd)":/mnt --workdir /mnt frolvlad/alpine-gcc gcc --static qq.c -o qq
</code></pre>

<p>You are passing one big string as the last parameter to Docker. Docker is trying to run a binary called  <code>sh -c 'javac example.java &amp;&amp; java example'</code> which obviously doesn't exist.</p>

<p>You want to run the <code>sh</code> binary with 2 arguments. <code>-c</code> and the shell script you want <code>sh</code> to run.</p>

<pre><code>return BASE_CMD + [
    folder_name + ":/mnt",
    "--workdir",
    "/mnt",
    "frolvlad/alpine-oraclejdk8:slim",
    "sh", 
    "-c",
    "javac " + file_name + " &amp;&amp; java " + file_name[:-5]
]
</code></pre>

<p>If a command line script becomes too complex, you can hide it behind a shell script</p>

<pre><code>#!/bin/sh
set -ue
javac $1
java ${1%.*}
</code></pre>

<p>Then </p>

<pre><code>$ compile_and_run.sh whatever.java
</code></pre>

python linux docker console