ZetCode

Python os.posix_spawnp Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.posix_spawnp function, which creates new processes using the POSIX spawn interface. We'll cover process creation, argument handling, file actions, and practical examples.

Basic Definitions

The os.posix_spawnp function creates a new process using the POSIX spawn interface. It searches for the executable in PATH like os.execvp.

Key parameters: path (executable name), argv (argument list), env (environment), file_actions (file operations), and attributes (process attributes). Returns the new process ID.

Basic Process Creation

The simplest use of os.posix_spawnp launches a program with arguments. This example runs the 'ls' command with '-l' flag to list files.

basic_spawn.py
import os

# Basic process creation
pid = os.posix_spawnp(
    "ls",  # Program to execute
    ["ls", "-l", "/"],  # Argument list
    os.environ  # Inherit environment
)

print(f"Created process with PID: {pid}")

# Wait for the process to complete
_, status = os.waitpid(pid, 0)
print(f"Process exited with status: {status}")

This example demonstrates the minimal parameters needed to spawn a process. The argv list must include the program name as the first element.

The function returns the PID of the new process, which we can wait for using os.waitpid to get its exit status.

Custom Environment Variables

We can specify custom environment variables when spawning a process. This example sets a special environment variable for the child process.

custom_env.py
import os

# Create custom environment
custom_env = os.environ.copy()
custom_env["MY_VAR"] = "special_value"

pid = os.posix_spawnp(
    "printenv",  # Program to show environment
    ["printenv", "MY_VAR"],  # Argument to print our var
    custom_env  # Our custom environment
)

# Wait for completion
_, status = os.waitpid(pid, 0)
print(f"Process exited with status: {status}")

This creates a copy of the current environment and adds our custom variable. The child process (printenv) will only see variables we explicitly provide.

Environment handling is important for security - never pass untrusted data directly to child processes.

File Actions (Redirection)

File actions allow modifying file descriptors before program execution. This example redirects stdout to a file using posix_spawn_file_actions_adddup2.

file_actions.py
import os

# Create file actions object
file_actions = os.posix_spawn_file_actions_t()
os.posix_spawn_file_actions_init(file_actions)

# Open output file
output_fd = os.open("output.txt", os.O_WRONLY | os.O_CREAT, 0o644)

# Redirect stdout (fd 1) to our file
os.posix_spawn_file_actions_adddup2(file_actions, output_fd, 1)

pid = os.posix_spawnp(
    "ls",
    ["ls", "-l", "/"],
    os.environ,
    file_actions
)

os.close(output_fd)
os.waitpid(pid, 0)

print("Command output written to output.txt")

We first create a file actions object, then add a dup2 operation that will make fd 1 (stdout) point to our output file. The child's output gets redirected.

Remember to close file descriptors you open, both in parent and child processes.

Process Attributes

Process attributes control various aspects of the new process. This example sets the process group and scheduling policy for the child process.

process_attributes.py
import os

# Create attributes object
attrs = os.posix_spawnattr_t()
os.posix_spawnattr_init(attrs)

# Set flags for what we want to modify
flags = os.POSIX_SPAWN_SETPGROUP | os.POSIX_SPAWN_SETSCHEDULER
os.posix_spawnattr_setflags(attrs, flags)

# Set process group to new group
os.posix_spawnattr_setpgroup(attrs, 0)

# Set scheduling policy to batch
os.posix_spawnattr_setschedpolicy(attrs, os.SCHED_BATCH)

pid = os.posix_spawnp(
    "sleep",
    ["sleep", "10"],
    os.environ,
    None,  # No file actions
    attrs
)

print(f"Created process with PID {pid} in new process group")
os.waitpid(pid, 0)

This demonstrates setting multiple process attributes. We specify which attributes we want to modify using flags, then set the actual attribute values.

Process attributes allow fine-grained control over the execution environment of the child process.

Error Handling

Proper error handling is crucial when spawning processes. This example shows how to handle various error conditions that might occur.

error_handling.py
import os
import sys

try:
    pid = os.posix_spawnp(
        "nonexistent_command",
        ["nonexistent_command"],
        os.environ
    )
except FileNotFoundError as e:
    print(f"Error: {e}", file=sys.stderr)
    sys.exit(1)
except OSError as e:
    print(f"OS error: {e}", file=sys.stderr)
    sys.exit(1)

try:
    _, status = os.waitpid(pid, 0)
    if os.WIFEXITED(status):
        print(f"Process exited with status {os.WEXITSTATUS(status)}")
    elif os.WIFSIGNALED(status):
        print(f"Process killed by signal {os.WTERMSIG(status)}")
except ChildProcessError:
    print("Child process already terminated", file=sys.stderr)

The first try block catches errors during process creation. The second handles waitpid errors and interprets the process exit status.

Always check for both normal exits (WIFEXITED) and signal-induced termination (WIFSIGNALED) when examining process status.

Combining File Actions and Attributes

This advanced example combines file actions and process attributes to create a process with redirected I/O and special scheduling.

combined_features.py
import os

# Set up file actions
file_actions = os.posix_spawn_file_actions_t()
os.posix_spawn_file_actions_init(file_actions)

# Redirect stdout and stderr to files
out_fd = os.open("stdout.txt", os.O_WRONLY | os.O_CREAT, 0o644)
err_fd = os.open("stderr.txt", os.O_WRONLY | os.O_CREAT, 0o644)

os.posix_spawn_file_actions_adddup2(file_actions, out_fd, 1)
os.posix_spawn_file_actions_adddup2(file_actions, err_fd, 2)

# Set up process attributes
attrs = os.posix_spawnattr_t()
os.posix_spawnattr_init(attrs)
os.posix_spawnattr_setflags(attrs, os.POSIX_SPAWN_SETSCHEDULER)
os.posix_spawnattr_setschedpolicy(attrs, os.SCHED_IDLE)

pid = os.posix_spawnp(
    "find",
    ["find", "/usr", "-name", "*.py"],
    os.environ,
    file_actions,
    attrs
)

os.close(out_fd)
os.close(err_fd)

print(f"Find process running with PID {pid}")
_, status = os.waitpid(pid, 0)
print(f"Process completed with status {status}")

This combines all major features: file descriptor manipulation, process scheduling, and proper cleanup. The find command runs with low priority.

Note how we redirect both stdout and stderr to different files and set the scheduling policy to IDLE for this non-critical background task.

Security Considerations

Best Practices

Source References

Author

My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.

List all Python tutorials.