HOWTO · C

C의 dup2 함수

이 기사에서는 C에서 dup2 함수를 사용하는 방법을 보여줍니다.

이 페이지의 내용

POSIX C에서 dup2(oldfd, newfd)는 newfd가 oldfd와 같은 열린 파일 설명을 참조하도록 합니다. 파일을 읽거나 쓰기 전에 표준 입력이나 출력을 리디렉션할 때 흔히 사용합니다. 호출이 실패하면 -1을 반환하고 errno를 설정하므로 반환값을 확인하세요.

dup2의 동작과 사용 시점

함수 선언은 int dup2(int oldfd, int newfd);이며 <unistd.h>를 통해 사용할 수 있습니다. 성공하면 newfd를 반환합니다. newfd가 이미 열려 있으면 dup2가 해당 파일 설명자를 닫고 그 번호를 다시 사용합니다. 닫기와 복제 작업은 원자적으로 이루어지므로, 다른 스레드와 함께 실행될 수 있는 코드에서 리디렉션할 때 유용합니다.

두 파일 설명자는 하나의 열린 파일 설명을 참조합니다. 따라서 파일 오프셋과 파일 상태 플래그를 공유하며, 어느 쪽으로 읽거나 쓰든 같은 오프셋이 이동합니다. 파일 설명자 플래그는 각 설명자에 따로 적용됩니다. dup2가 만든 새 파일 설명자는 oldfd의 FD_CLOEXEC를 상속하지 않습니다.

oldfd와 newfd가 같으면 dup2는 그 설명자를 닫지 않고 그대로 반환합니다. 이때 설명자는 이미 유효해야 합니다. 파일 설명자 번호는 프로세스마다 다르고 어떤 설명자가 열려 있는지에 따라 달라집니다. 파일을 열면 항상 설명자 3을 받는다거나 모든 프로세스에 열린 설명자가 정확히 세 개 있다고 가정하지 마세요.

dup2로 표준 출력 리디렉션하기

쓰기용 대상을 열고 그 파일 설명자를 STDOUT_FILENO에 복제합니다. 이 간결한 프로그램은 출력을 임시 파일로 리디렉션한 다음 원래 표준 출력을 복원하고, 저장된 한 줄을 출력해 결과를 확인합니다. POSIX 임시 파일 및 파일 설명자 함수를 사용합니다.

이 예제를 실행하세요:

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#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

제공된 실행에서는 Captured: Hello through dup2가 캡처되었습니다. 프로그램은 첫 번째 줄을 임시 파일에 쓰고, 표준 출력을 복원한 뒤 저장된 바이트를 읽어 캡처한 줄을 출력합니다. 실제 프로그램에서는 데이터에 적절한 경로와 권한을 선택하세요. mkstemp 예제는 보호된 임시 파일을 만듭니다.

dup2로 표준 입력 리디렉션하기

프로그램이 표준 입력을 통해 파일을 읽게 하려면 파일을 읽기 전용으로 열고 해당 파일 설명자를 STDIN_FILENO에 복제합니다. 호출이 성공하면 stdin에서 읽는 fgets 같은 함수가 그 파일에서 읽습니다. 표준 입력이 이제 같은 열린 파일 설명을 참조하므로 복제 후 open이 반환한 파일 설명자는 닫아도 됩니다.

이 완전한 예제는 임시 입력 파일을 만들고 표준 입력을 그 파일로 리디렉션한 다음 fgets로 한 줄을 읽습니다. 읽은 내용을 확인할 수 있도록 줄을 출력합니다. 예제에서는 임시 파일이 입력으로 사용됩니다. 기존 파일에 이 패턴을 적용할 때는 파일 내용을 바꾸거나 fgets 처리 로직을 조정하세요.

이 섹션의 코드와 표시되는 레이블 또는 출력은 영어로 유지됩니다.

#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;
}

실제 입력 경로를 사용하는 open 방식은 int input = open("input.txt", O_RDONLY);입니다. 반환값이 -1인지 확인하고, dup2(input, STDIN_FILENO)를 호출한 뒤 성공하면 input을 닫으세요. 이후 stdin에서 읽으면 해당 파일의 내용을 읽습니다. 완전한 예제에서는 같은 순서로 임시 파일에 쓴 줄을 읽습니다. 파일 열기, 복제 또는 읽기에 실패하면 오류를 처리하고 코드가 여전히 소유한 모든 파일 설명자를 닫으세요.

dup과 dup2의 차이

두 함수 모두 같은 열린 파일 설명을 참조하는 파일 설명자를 하나 더 만듭니다. dup(oldfd)는 현재 사용 가능한 번호 중 가장 낮은 파일 설명자를 선택해 반환합니다. dup2(oldfd, newfd)는 지정한 번호를 대상으로 하며, 대상이 열려 있으면 먼저 닫습니다. 아무 번호나 사용해도 되면 dup을 쓰고, 보통 표준 입력이나 출력처럼 특정 파일 설명자가 원본을 참조해야 하면 dup2를 사용하세요.

예를 들어 int copy = dup(file);은 새 파일 설명자를 반환하거나 실패 시 -1을 반환합니다. copy를 사용하기 전에 반환값을 확인하고, 두 설명자가 모두 필요 없어지면 둘 다 닫으세요. 반환되는 번호가 3이라고 보장되지는 않습니다. 프로세스에서 이미 열려 있는 파일 설명자에 따라 달라집니다.

오류와 파일 설명자 수명 처리

반환된 파일 설명자를 사용하기 전에 모든 open, dup, dup2 호출의 결과를 확인하세요. 실패하면 -1을 반환하고 errno를 설정합니다. perror("dup2")를 사용하면 현재 오류를 출력할 수 있습니다. 실패한 결과를 파일 설명자로 사용하지 마세요. 여러 단계로 구성된 리디렉션 중 호출이 실패하면 코드가 열거나 저장한 파일 설명자를 닫으세요. 표준 입력이나 출력이 이미 변경되었고 프로그램이 원래 스트림을 계속 사용해야 한다면 이를 복원하세요.

리디렉션에 성공한 뒤 원본 파일 설명자를 닫아도 리디렉션은 취소되지 않습니다. 표준 파일 설명자는 열린 상태로 유지되며, 동일한 열린 파일 설명을 참조합니다. 저장한 파일 설명자와 다른 복제본은 더 이상 필요하지 않을 때 닫으세요. STDOUT_FILENO를 변경하거나 복원하기 전에 버퍼에 남은 출력을 비우세요. C 표준 입출력 버퍼는 커널의 파일 설명자 작업과 별개입니다. 이런 점을 확인하면 파일 설명자 번호를 가정하지 않고도 교체와 정리를 예측 가능하게 처리할 수 있습니다.