HOWTO · C
La función dup2 en C
Este artículo muestra cómo utilizar la función dup2 en C
En esta página
En C POSIX, dup2(oldfd, newfd) hace que newfd se refiera a la misma descripción de archivo abierto que oldfd. Se usa habitualmente para redirigir la entrada o la salida estándar antes de leer de un archivo o escribir en él. Comprueba el valor devuelto: si la llamada falla, devuelve -1 y establece errno.
Qué hace dup2 y cuándo usarla
La declaración es int dup2(int oldfd, int newfd); y está disponible mediante <unistd.h>. Si tiene éxito, devuelve newfd. Si newfd ya estaba abierto, dup2 lo cierra y reutiliza ese descriptor. Esta sustitución es atómica en lo que respecta a la operación de cerrar y duplicar, por lo que resulta útil para redirigir flujos en código que puede ejecutarse junto con otros hilos.
Ambos descriptores se refieren a una misma descripción de archivo abierto. Por tanto, comparten el desplazamiento del archivo y sus indicadores de estado: leer o escribir mediante cualquiera de los dos hace avanzar el mismo desplazamiento. Los indicadores del descriptor son propios de cada descriptor; el nuevo descriptor creado por dup2 no hereda FD_CLOEXEC de oldfd.
Si oldfd y newfd son iguales, dup2 devuelve ese descriptor sin cerrarlo. El descriptor ya debe ser válido. Los números de descriptor son específicos de cada proceso y dependen de qué descriptores estén abiertos; no des por hecho que abrir un archivo siempre devuelve el descriptor 3 ni que todos los procesos tienen exactamente tres descriptores abiertos.
Redirigir la salida estándar con dup2
Abre un destino para escritura y, a continuación, duplica su descriptor sobre STDOUT_FILENO. Este programa breve redirige la salida a un archivo temporal, restaura la salida estándar original e imprime la línea capturada para que el resultado sea visible. Usa funciones POSIX para archivos temporales y descriptores.
Ejecuta este ejemplo:
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
#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
La ejecución proporcionada capturó Captured: Hello through dup2. El programa escribe su primera línea en el archivo temporal, restaura la salida estándar, lee los bytes guardados e imprime esa línea capturada. En un programa de producción, elige una ruta de destino y permisos adecuados para los datos; el ejemplo con mkstemp crea un archivo temporal privado.
Redirigir la entrada estándar con dup2
Para que un programa lea un archivo a través de la entrada estándar, ábrelo en modo de solo lectura y duplica su descriptor sobre STDIN_FILENO. Tras una llamada exitosa, funciones como fgets que leen de stdin leen de ese archivo. Puedes cerrar el descriptor devuelto por open después de duplicarlo, porque la entrada estándar ya se refiere a la misma descripción de archivo abierto.
Este ejemplo completo crea un archivo temporal de entrada, redirige la entrada estándar a ese archivo y lee una línea con fgets. Luego imprime la línea para mostrar lo que leyó. El archivo temporal es la entrada del ejemplo; al adaptar este patrón a un archivo existente, sustituye su contenido o la lógica de fgets según corresponda.
El código y las etiquetas o salidas visibles de esta sección se mantienen en inglés.
#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;
}
La forma basada en open para una ruta de entrada real es int input = open("input.txt", O_RDONLY);; comprueba si devuelve -1, llama a dup2(input, STDIN_FILENO) y, si tiene éxito, cierra input. Las lecturas posteriores de stdin consumen ese archivo. En el ejemplo completo, la misma secuencia lee la línea escrita en el archivo temporal. Si falla la apertura, la duplicación o la lectura, gestiona el error y cierra todos los descriptores que aún estén bajo el control de tu código.
Diferencias entre dup y dup2
Ambas funciones crean otro descriptor para la misma descripción de archivo abierto. dup(oldfd) elige y devuelve el descriptor disponible con el número más bajo. dup2(oldfd, newfd) apunta al número que especifiques y primero cierra el destino si ya está abierto. Usa dup cuando sirva cualquier número disponible; usa dup2 cuando un descriptor concreto, normalmente la entrada o salida estándar, deba referirse al origen.
Por ejemplo, int copy = dup(file); devuelve un nuevo descriptor o -1 si falla. Comprueba el resultado antes de usar copy y cierra ambos descriptores cuando ya no los necesites. No está garantizado que el número devuelto sea 3; depende de los descriptores abiertos del proceso.
Gestionar errores y la vida útil de los descriptores
Comprueba el resultado de cada llamada a open, dup y dup2 antes de usar el descriptor devuelto. Si una llamada falla, devuelve -1 y establece errno; perror("dup2") puede informar del error actual. No uses un resultado fallido como descriptor. Si falla una llamada dentro de una secuencia de redirección de varios pasos, cierra los descriptores que tu código haya abierto o guardado y restaura la entrada o la salida estándar si ya la cambiaste y el programa debe continuar con el flujo original.
Tras una redirección exitosa, cerrar el descriptor de origen original no deshace la redirección: el descriptor estándar permanece abierto y se refiere a la descripción de archivo abierto compartida. Cierra los descriptores guardados y otros duplicados cuando dejen de ser necesarios. Vacía los búferes de salida antes de cambiar o restaurar STDOUT_FILENO; los búferes de E/S estándar de C son independientes de las operaciones del núcleo sobre descriptores. Estas comprobaciones hacen predecibles la sustitución y la limpieza de descriptores, sin depender de números concretos.