ZetCode

Python os.dup2 Function

Last modified April 11, 2025

This comprehensive guide explores Python's os.dup2 function, which duplicates file descriptors for I/O redirection. We'll cover descriptor management, common use cases, and practical examples.

Basic Definitions

The os.dup2 function duplicates a file descriptor to another specified descriptor number. It closes the target descriptor first if needed.

Key parameters: fd (source descriptor), fd2 (target descriptor). Returns the new descriptor number. Used for I/O redirection and descriptor management.

Basic File Descriptor Duplication

This example demonstrates the fundamental usage of os.dup2 to duplicate a file descriptor. We'll create a file and duplicate its descriptor.

basic_dup2.py
import os

# Create a file and get its descriptor
with open("example.txt", "w") as f:
    fd = f.fileno()
    print(f"Original file descriptor: {fd}")

    # Duplicate the descriptor to fd 10
    new_fd = os.dup2(fd, 10)
    print(f"New file descriptor: {new_fd}")

    # Verify both descriptors point to same file
    os.write(fd, b"Hello from original fd\n")
    os.write(new_fd, b"Hello from duplicated fd\n")

# Check file contents
with open("example.txt") as f:
    print(f.read())

This shows how os.dup2 creates an alias for a file descriptor. Both the original and new descriptor can write to the same file.

The example uses descriptor 10 for clarity, but normally you'd use available descriptors. Always close duplicated descriptors when done.

Redirecting Standard Output

A common use of os.dup2 is redirecting stdout to a file. This example captures all print statements to a log file.

redirect_stdout.py
import os
import sys

# Open a log file
log_file = open("output.log", "w")

# Save the original stdout descriptor
original_stdout = os.dup(1)

# Redirect stdout to the log file
os.dup2(log_file.fileno(), 1)

print("This will go to the log file")
print("So will this line")

# Restore original stdout
os.dup2(original_stdout, 1)
log_file.close()

print("Back to normal output")

This technique is useful for capturing program output without modifying print statements. The original stdout is preserved for later restoration.

Note that file descriptor 1 is the standard output descriptor in Unix-like systems. Similar redirection works for stdin (0) and stderr (2).

Combining Output Streams

This example demonstrates combining stderr and stdout into a single stream using os.dup2. Both outputs will appear together.

combine_streams.py
import os
import sys

# Save original stderr
original_stderr = os.dup(2)

# Redirect stderr to stdout
os.dup2(1, 2)

print("Standard output message")
print("Error message", file=sys.stderr)

# Restore original stderr
os.dup2(original_stderr, 2)
os.close(original_stderr)

print("Back to separate streams")
print("Now errors go separately", file=sys.stderr)

After redirection, both regular output and error messages appear on stdout. This is useful when you want to capture all output in one stream.

Remember to restore the original descriptors when done to avoid confusing behavior in later code or libraries.

Creating a Custom Output Pipe

This advanced example creates a pipe and redirects output to it using os.dup2. The parent process can then read the child's output.

custom_pipe.py
import os
import sys

# Create a pipe
read_fd, write_fd = os.pipe()

pid = os.fork()

if pid == 0:  # Child process
    os.close(read_fd)
    
    # Redirect stdout to the write end of the pipe
    os.dup2(write_fd, 1)
    
    print("Child process writing to pipe")
    sys.stdout.flush()
    os._exit(0)
else:  # Parent process
    os.close(write_fd)
    
    # Read from the read end of the pipe
    print("Parent process received:")
    while True:
        data = os.read(read_fd, 1024)
        if not data:
            break
        print(data.decode(), end="")
    
    os.waitpid(pid, 0)

The child process writes to stdout, which is redirected to the pipe. The parent reads from the other end of the pipe.

This technique is fundamental for inter-process communication and capturing output from child processes.

Temporary Output Suppression

This example shows how to temporarily suppress all output by redirecting to /dev/null using os.dup2.

suppress_output.py
import os
import sys

# Open /dev/null
devnull = open(os.devnull, "w")

# Save original stdout and stderr
original_stdout = os.dup(1)
original_stderr = os.dup(2)

# Redirect both to /dev/null
os.dup2(devnull.fileno(), 1)
os.dup2(devnull.fileno(), 2)

print("This won't appear anywhere")
print("Neither will this error", file=sys.stderr)

# Restore original descriptors
os.dup2(original_stdout, 1)
os.dup2(original_stderr, 2)
devnull.close()

print("Output is back to normal")
print("Errors too", file=sys.stderr)

This technique is useful when you need to silence output from certain sections of code or from third-party libraries.

Note that this only affects the current process. Child processes will still have their own stdout/stderr unless similarly redirected.

Implementing a Tee-like Function

This example creates a function that duplicates output to both stdout and a file simultaneously, similar to the Unix tee command.

tee_function.py
import os
import sys

class Tee:
    def __init__(self, filename):
        self.file = open(filename, "w")
        self.stdout = sys.stdout
        self.fd = self.stdout.fileno()
        
        # Save original stdout
        self.saved_fd = os.dup(self.fd)
        
        # Create pipe
        self.pipe_out, self.pipe_in = os.pipe()
        
        # Replace stdout with pipe in
        os.dup2(self.pipe_in, self.fd)
        
        # Start reader thread
        self.running = True
        import threading
        self.thread = threading.Thread(target=self.reader)
        self.thread.start()
    
    def reader(self):
        while self.running:
            data = os.read(self.pipe_out, 1024)
            if not data:
                break
            self.file.write(data.decode())
            self.stdout.write(data.decode())
            self.file.flush()
            self.stdout.flush()
    
    def close(self):
        self.running = False
        os.close(self.pipe_out)
        os.close(self.pipe_in)
        os.dup2(self.saved_fd, self.fd)
        os.close(self.saved_fd)
        self.file.close()

# Usage
print("Before Tee")
tee = Tee("output.log")
print("During Tee - goes to both console and file")
print("Another line")
tee.close()
print("After Tee - back to normal")

This implementation uses a pipe and a background thread to capture and duplicate all output. The Tee class handles the redirection mechanics.

The solution is more complex than simple redirection but demonstrates the power of combining os.dup2 with other system features.

Handling File Descriptor Leaks

This example shows proper handling of file descriptors when using os.dup2 to prevent resource leaks.

leak_prevention.py
import os

def safe_redirection(filename):
    # Open target file
    target_fd = os.open(filename, os.O_WRONLY | os.O_CREAT)
    
    try:
        # Save original stdout
        original_stdout = os.dup(1)
        
        try:
            # Redirect stdout
            os.dup2(target_fd, 1)
            
            # Perform operations
            print("This goes to the file")
            os.system("echo 'System command output'")
            
        finally:
            # Restore stdout even if exceptions occur
            os.dup2(original_stdout, 1)
            os.close(original_stdout)
    finally:
        # Close target file descriptor
        os.close(target_fd)

# Test the function
safe_redirection("safe_output.txt")

# Verify output is back to normal
print("This should appear on console")

The example demonstrates proper resource cleanup using try/finally blocks. This ensures descriptors are closed even if errors occur during execution.

Always follow this pattern when working with low-level file descriptors to prevent resource leaks that could crash your program.

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.