HOWTO · C

The dup2 Function in C

Learn how dup2 duplicates file descriptors in C, redirects standard input and output, differs from dup, and how to handle errors and descriptor lifetimes.

On this page

In POSIX C, dup2(oldfd, newfd) makes newfd refer to the same open file description as oldfd. It is commonly used to redirect standard input or output before reading from or writing to a file. Check the return value, since a failed call returns -1 and sets errno.

What dup2 Does and When to Use It

The declaration is int dup2(int oldfd, int newfd);, available through <unistd.h>. On success, it returns newfd. If newfd was open, dup2 closes it and reuses that descriptor. This replacement is atomic with respect to the close-and-duplicate operation, which makes it useful for redirection in code that may run alongside other threads.

The two descriptors refer to one open file description. They therefore share the file offset and file status flags: reading or writing through either descriptor advances the same offset. Descriptor flags belong to each descriptor; the new descriptor created by dup2 does not inherit FD_CLOEXEC from oldfd.

If oldfd and newfd are equal, dup2 returns that descriptor without closing it. The descriptor must already be valid. File descriptor numbers are process-specific and depend on which descriptors are open; do not assume that opening a file always returns descriptor 3, or that every process has exactly three open descriptors.

Redirect Standard Output with dup2

Open a destination for writing, then duplicate it onto STDOUT_FILENO. This compact program redirects output to a temporary file, restores the original standard output, and prints the captured line so the result is visible. It uses POSIX temporary-file and descriptor functions.

Run this example:

#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    char path[] = "/tmp/dup2-demo-XXXXXX";
    int file = mkstemp(path);
    if (file == -1) return 1;

    int saved_stdout = dup(STDOUT_FILENO);
    if (saved_stdout == -1) {
        close(file);
        unlink(path);
        return 1;
    }

    if (dup2(file, STDOUT_FILENO) == -1) {
        close(saved_stdout);
        close(file);
        unlink(path);
        return 1;
    }

    int write_ok = puts("Hello through dup2") != EOF && fflush(stdout) == 0;
    int restore_ok = dup2(saved_stdout, STDOUT_FILENO) != -1;
    close(saved_stdout);
    if (!write_ok || !restore_ok) {
        close(file);
        unlink(path);
        return 1;
    }

    if (lseek(file, 0, SEEK_SET) == (off_t)-1) {
        close(file);
        unlink(path);
        return 1;
    }

    char buffer[64];
    ssize_t length = read(file, buffer, sizeof(buffer) - 1);
    close(file);
    unlink(path);
    if (length < 0) return 1;
    buffer[length] = '\0';
    if (printf("Captured: %s", buffer) < 0) return 1;
    return 0;
}
Captured: Hello through dup2

The supplied run captured Captured: Hello through dup2. The program writes its first line to the temporary file, restores standard output, reads the saved bytes, and prints that captured line. In a production program, choose a destination path and permissions appropriate to the data; the mkstemp example creates a private temporary file.

Redirect Standard Input with dup2

To make a program read from a file through standard input, open the file read-only and duplicate its descriptor onto STDIN_FILENO. After a successful call, functions such as fgets that read from stdin read from that file. The descriptor returned by open can be closed after duplication because standard input now refers to the same open file description.

This complete example creates a temporary input file, redirects standard input to it, and reads one line with fgets. It prints the line so you can see what was read. The temporary file is the example’s input; replace its contents or the fgets logic when adapting the pattern to an existing file.

#define _POSIX_C_SOURCE 200809L
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(void) {
    char path[] = "/tmp/dup2-input-XXXXXX";
    int input = mkstemp(path);
    if (input == -1) return 1;

    const char line[] = "Read through redirected stdin\n";
    ssize_t written = write(input, line, sizeof line - 1);
    if (written != (ssize_t)(sizeof line - 1) || lseek(input, 0, SEEK_SET) == (off_t)-1) {
        close(input);
        unlink(path);
        return 1;
    }

    if (dup2(input, STDIN_FILENO) == -1) {
        close(input);
        unlink(path);
        return 1;
    }
    close(input);
    unlink(path);

    char buffer[80];
    if (fgets(buffer, sizeof buffer, stdin) == NULL) return 1;
    if (printf("Read: %s", buffer) < 0) return 1;
    return 0;
}

The open-based form for a real input path is int input = open("input.txt", O_RDONLY);; check for -1, call dup2(input, STDIN_FILENO), then close input after success. Subsequent reads from stdin consume that file. In the complete example, the same sequence reads the line written to the temporary file. If opening, duplicating, or reading fails, handle the error and close every descriptor your code still owns.

How dup Differs from dup2

Both functions create another descriptor for the same open file description. dup(oldfd) chooses and returns the lowest-numbered descriptor that is currently available. dup2(oldfd, newfd) targets the number you specify, closing an open target first. Use dup when any available number works; use dup2 when a particular descriptor, commonly standard input or output, must refer to the source.

For example, int copy = dup(file); returns a new descriptor or -1 on failure. Check it before using copy, and close both descriptors when neither is needed. The returned number is not guaranteed to be 3; its value depends on the process’s open descriptors.

Handle Errors and Descriptor Lifetimes

Check every open, dup, and dup2 result before using the returned descriptor. A failure returns -1 and sets errno; perror("dup2") can report the current error. Do not use a failed result as a descriptor. If a call in a multi-step redirection sequence fails, close descriptors your code has opened or saved, and restore standard input or output if it has already been changed and the program must continue with the original stream.

After successful redirection, closing the original source descriptor does not undo the redirection: the standard descriptor remains open and refers to the shared open file description. Close saved descriptors and other duplicates when they are no longer needed. Flush buffered output before changing or restoring STDOUT_FILENO; C stdio buffers are separate from the kernel’s descriptor operations. These checks make descriptor replacement and cleanup predictable while avoiding assumptions about descriptor numbers.