Skip to main content
ConTree SDK provides multiple ways to execute commands in containers, from simple shell commands to complex workflows with file handling and custom I/O.

Basic Command Execution

You can run commands using shell syntax or by specifying command and arguments separately:
image = await client.images.use("busybox:latest")
print(f"Using {image=}")

result = await image.run(shell="echo 'Hello World'")
print(f"Simple echo: {result.stdout=}, {result.stderr=}, {result.exit_code=}")

result = await image.run(shell="pwd")
print(f"Current directory: {result.stdout=}, {result.exit_code=}")

result = await image.run(shell="ls -la")
print(f"Directory listing: {result.stdout=}, {result.exit_code=}")

result = await image.run(shell="cat -", stdin="Hello from stdin\n")
print(f"Cat with stdin: {result.stdout=}, {result.exit_code=}")

result = await image.run(shell="echo 'Error message' >&2; exit 1")
print(f"Error command: {result.stdout=}, {result.stderr=}, {result.exit_code=}")
See ContreeImage.run() and ContreeSession.run() for all options.

Command Execution Mode

You can execute commands by specifying the executable path and arguments separately:
image = await client.images.use("alpine:3.20", strict=True)
print(f"Pulled {image=}")

print("\nExample 1: Simple command execution")
result = await image.run("/bin/echo", args=["Hello from command parameter!"])
print(f"Result: {result.stdout=}, {result.exit_code=}")

print("\nExample 2: Command with arguments")
result = await image.run("/bin/ls", args=["-la", "/tmp"])
print(f"Result: {result.stdout=}, {result.exit_code=}")

print("\nExample 3: Command with environment variables")
result = await image.run("/bin/printenv", args=["MY_VAR"], env={"MY_VAR": "test_value"})
print(f"Result: {result.stdout=}, {result.exit_code=}")
See ContreeImage.run() and ContreeSession.run() for command execution details.

Command vs Shell Mode

  • Command mode: Use command="/bin/ls" with args=["-la", "/tmp"] for direct execution without shell interpretation
  • Shell mode: Use shell="ls -la /tmp" for shell commands with pipes, redirects, and wildcards
  • Environment variables: Pass env={"VAR": "value"} to set environment for command execution

Working with Files

You can upload and use files in your commands by specifying local file paths or pre-uploaded file objects:
image = await client.images.use("busybox:latest")
print(f"Using {image=}")

print("\nExample 1: Local file upload to image")
with NamedTemporaryFile(mode="w", suffix=".txt") as test_file:
    test_file.write("some txt file\nsecond line\n\nlast line\n")
    test_file.flush()

    result = await image.run(shell=f"cat /{test_file.name.split('/')[-1]} | grep line", files=[test_file.name])
    print(f"Run with local file: {result.stdout=}, {result.exit_code=}")

print("\nExample 2: Upload file via contree.files and use in image")
with NamedTemporaryFile(mode="w", suffix=".sh") as script_file:
    script_file.write("#!/bin/sh\necho 'Hello from uploaded script'\necho 'Working directory:'\npwd\n")
    script_file.flush()

    uploaded_file = await client.files.upload(script_file.name)
    print(f"Uploaded file: {uploaded_file=}")

    result = await image.run(shell="sh /file.sh", files={"file.sh": uploaded_file})
    print(f"Run with uploaded file: {result.stdout=}, {result.stderr=}, {result.exit_code=}")

print("\nExample 3: Bake files into a new image with apply_files")
with NamedTemporaryFile(mode="w", suffix=".txt") as f:
    f.write("hello from baked file\n")
    f.flush()
    baked = await image.apply_files({"baked.txt": f.name})
    result = await baked.run(shell="cat /baked.txt")
    print(f"File is present in new image: {result.stdout=}")

print("\nExample 4: Multiple files working together")
with (
    NamedTemporaryFile(mode="w", suffix=".txt") as data_file,
    NamedTemporaryFile(mode="w", suffix=".sh") as script_file,
):
    data_file.write("apple\nbanana\ncherry\ndate\n")
    data_file.flush()

    script_file.write(
        "#!/bin/bash\necho 'Processing data:'\ncat /data.txt | grep -E '^[ab]'"
        "\necho 'Found items starting with a or b'"
    )
    script_file.flush()

    result = await image.run(
        shell="chmod +x /script.sh && sh /script.sh",
        files={"data.txt": data_file.name, "script.sh": script_file.name},
    )
    print(f"Multiple files result: {result.stdout=}, {result.stderr=}, {result.exit_code=}")

File Upload Methods

You can provide files to commands in several ways:
  • Local file paths: files=["/path/to/local/file.txt"] - Upload files directly
  • File mapping: files={"dest.txt": "/local/source.txt"} - Upload with custom names
  • Pre-uploaded files: files={"script.sh": uploaded_file_object} - Use files uploaded via client.files.upload()

Advanced I/O Handling

You can use Python I/O objects for more sophisticated input/output handling:
image = await client.images.use("busybox:latest")
print(f"Using {image=}")

print("\nExample 1: StringIO for stdin and stdout")
stdin_io = StringIO("apple\nbanana\ncherry\ndate\n")
stdout_io = StringIO()

result = await image.run(shell="grep 'a' | sort", stdin=stdin_io, stdout=stdout_io)
print(f"StringIO result: exit_code={result.exit_code}")
print(f"Output in StringIO: {stdout_io.getvalue()=}")
print(f"result.stdout is the StringIO object: {result.stdout is stdout_io}")

print("\nExample 2: PIPE for stderr capture")
result = await image.run(shell="echo 'to stdout'; echo 'to stderr' >&2; exit 0", stderr=PIPE)
print(f"PIPE stderr: {result.stdout=}")
print(f"Stderr content: {result.stderr.read().decode()=}")
print(f"Stderr type: {type(result.stderr).__name__}")

print("\nExample 3: Output to bytes")
result = await image.run(shell="echo 'Hello bytes world'", stdout=bytes)
print(f"Bytes output: {result.stdout=}")
print(f"Output type: {type(result.stdout).__name__}")

print("\nExample 4: open() file object for input")
with NamedTemporaryFile(mode="w", suffix=".txt") as temp_file:
    temp_file.write("line1\nline2\nline3\n")
    temp_file.flush()

    with open(temp_file.name) as file_obj:
        result = await image.run(shell="wc -l", stdin=file_obj)
        print(f"File object input: {result.stdout=}, {result.exit_code=}")

print("\nExample 5: BytesIO for binary data")
binary_data = BytesIO(b"binary\ndata\nlines\n")

result = await image.run(shell="wc -l", stdin=binary_data)
print(f"BytesIO input: {result.stdout=}, {result.exit_code=}")
See run() for I/O parameter details.

Supported I/O Types

  • StringIO: For text-based input/output
  • BytesIO: For binary data handling
  • File objects: Use open() file handles directly
  • PIPE: Capture stderr/stdout as byte streams
  • bytes type: Get output as bytes instead of strings

Subprocess-like Interface (Sync Only)

You can use a subprocess-like interface for more control over process execution:
image = client.images.use("busybox:latest")
print(f"Using {image=}")

print("\nExample 1: Basic popen with wait()")
process = image.popen(["/bin/ls", "-la"], cwd="/bin")
process.wait()
print(f"Process completed: returncode={process.returncode}")
print(f"Output: {process.stdout[:100]}...")

print("\nExample 2: Shell command with stdout and stderr")
process = image.popen("echo 'Hello stdout' && echo 'Hello stderr' >&2", shell=True)
process.wait()
print(f"Stdout: {process.stdout=}")
print(f"Stderr: {process.stderr=}")

print("\nExample 3: Using communicate() for input/output")
process = image.popen(["/bin/grep", "apple"])
stdout, stderr = process.communicate(input="apple\nbanana\ncherry\napple pie\n")
print(f"Grep results: {stdout=}")
print(f"Return code: {process.returncode=}")

print("\nExample 4: Environment variables")
process = image.popen(
    "echo $MY_VAR && echo $ANOTHER_VAR", shell=True, env={"MY_VAR": "hello_world", "ANOTHER_VAR": "test_value"}
)
process.wait()
print(f"Environment output: {process.stdout=}")

print("\nExample 5: Error handling")
process = image.popen(["/bin/ls", "/nonexistent"])
process.wait()
print(f"Error case: returncode={process.returncode}")
print(f"Error output: {process.stderr=}")
See ContreeProcessSync for the full subprocess API.

Popen Features

  • Process control: Use wait(), communicate(), and check returncode
  • Environment variables: Pass custom env dictionary
  • Working directory: Set cwd parameter
  • Shell commands: Enable with shell=True
  • Error handling: Check returncode and stderr for failures

Command Parameters

Core Parameters

  • shell: Execute as shell command (e.g., "ls -la | grep txt")
  • command: Executable path (e.g., "/bin/ls")
  • args: Command arguments as tuple (e.g., ("-la", "/tmp"))
  • stdin: Input data (string, bytes, or I/O object)
  • env: Environment variables as dictionary

I/O Parameters

  • stdout: Redirect stdout (StringIO, BytesIO, file path, or bytes)
  • stderr: Redirect stderr (StringIO, BytesIO, PIPE, or bytes)
  • files: Upload files (list of paths or dict mapping)

Execution Parameters

  • cwd: Working directory inside container
  • disposable: Whether to persist changes (default: True for runs, False for sessions)
  • tag: Tag to assign to the resulting image after execution (e.g. tag="myapp:v2")

Result Objects

Command execution returns result objects with:
  • stdout: Command output as string (or specified type)
  • stderr: Error output as string (or specified type)
  • exit_code: Process exit code (0 = success)
  • uuid: UUID of the resulting image state