HOWTO · C
A função dup2 em C
Este artigo demonstra como usar a função dup2 em C
Nesta página
Em C POSIX, dup2(oldfd, newfd) faz com que newfd se refira à mesma descrição de arquivo aberto que oldfd. É comum usá-la para redirecionar a entrada ou a saída padrão antes de ler ou gravar em um arquivo. Verifique o valor retornado: em caso de falha, a chamada retorna -1 e define errno.
O que dup2 faz e quando usá-la
A declaração é int dup2(int oldfd, int newfd); e está disponível por meio de <unistd.h>. Em caso de sucesso, a função retorna newfd. Se newfd já estiver aberto, dup2 o fecha e reutiliza esse descritor. Essa substituição é atômica em relação à operação de fechar e duplicar, o que torna a função útil para redirecionamentos em código que pode ser executado junto com outras threads.
Os dois descritores apontam para uma única descrição de arquivo aberto. Por isso, compartilham o deslocamento do arquivo e os sinalizadores de estado do arquivo: uma leitura ou gravação por qualquer um deles avança o mesmo deslocamento. Os sinalizadores do descritor são próprios de cada descritor; o novo descritor criado por dup2 não herda FD_CLOEXEC de oldfd.
Se oldfd e newfd forem iguais, dup2 retorna esse descritor sem fechá-lo. O descritor já precisa ser válido. Os números dos descritores de arquivo são específicos de cada processo e dependem dos descritores que estão abertos. Portanto, não presuma que abrir um arquivo sempre retorna o descritor 3 nem que todo processo tenha exatamente três descritores abertos.
Redirecionar a saída padrão com dup2
Abra um destino para gravação e, em seguida, duplique seu descritor para STDOUT_FILENO. Este programa compacto redireciona a saída para um arquivo temporário, restaura a saída padrão original e imprime a linha capturada para que o resultado fique visível. Ele usa funções POSIX para arquivos temporários e descritores.
Execute este exemplo:
O código e os rótulos ou saídas exibidos nesta seção permanecem em 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
A execução fornecida capturou Captured: Hello through dup2. A primeira linha do programa é gravada no arquivo temporário; em seguida, a saída padrão é restaurada, os bytes armazenados são lidos e a linha capturada é impressa. O rótulo Captured: permanece em inglês na saída do exemplo. Em um programa de produção, escolha um caminho de destino e permissões adequados aos dados; o exemplo com mkstemp cria um arquivo temporário privado.
Redirecionar a entrada padrão com dup2
Para fazer um programa ler um arquivo pela entrada padrão, abra o arquivo somente para leitura e duplique seu descritor para STDIN_FILENO. Depois que a chamada tiver sucesso, funções que leem de stdin, como fgets, passam a ler desse arquivo. O descritor retornado por open pode ser fechado após a duplicação, pois a entrada padrão passa a se referir à mesma descrição de arquivo aberto.
Este exemplo completo cria um arquivo temporário de entrada, redireciona a entrada padrão para ele e lê uma linha com fgets. A linha é impressa para mostrar o que foi lido. O arquivo temporário serve como entrada do exemplo; ao adaptar esse padrão para um arquivo existente, substitua o conteúdo ou ajuste a lógica de fgets.
O código e os rótulos ou saídas exibidos nesta seção permanecem em 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;
}
A forma com open para um caminho de entrada real é int input = open("input.txt", O_RDONLY);; verifique se o resultado é -1, chame dup2(input, STDIN_FILENO) e, após o sucesso, feche input. As leituras seguintes de stdin consomem esse arquivo. No exemplo completo, a mesma sequência lê a linha gravada no arquivo temporário. Se a abertura, a duplicação ou a leitura falhar, trate o erro e feche todos os descritores que ainda pertencem ao seu código.
Diferenças entre dup e dup2
As duas funções criam outro descritor para a mesma descrição de arquivo aberto. dup(oldfd) escolhe e retorna o descritor disponível de menor número. dup2(oldfd, newfd) usa o número especificado e primeiro fecha o destino, caso esteja aberto. Use dup quando qualquer número disponível servir; use dup2 quando um descritor específico, geralmente a entrada ou a saída padrão, precisar apontar para a origem.
Por exemplo, int copy = dup(file); retorna um novo descritor ou -1 em caso de falha. Verifique o resultado antes de usar copy e feche os dois descritores quando não precisar mais deles. Não há garantia de que o número retornado seja 3; ele depende dos descritores abertos no processo.
Tratar erros e o tempo de vida dos descritores
Verifique o resultado de cada chamada a open, dup e dup2 antes de usar o descritor retornado. Em caso de falha, a chamada retorna -1 e define errno; perror("dup2") pode informar o erro atual. Não use um resultado de falha como descritor. Se uma chamada falhar durante uma sequência de redirecionamento com várias etapas, feche os descritores que seu código abriu ou salvou. Se a entrada ou a saída padrão já tiver sido alterada e o programa precisar continuar usando o fluxo original, restaure-a.
Depois de um redirecionamento bem-sucedido, fechar o descritor de origem original não desfaz o redirecionamento: o descritor padrão continua aberto e aponta para a descrição de arquivo aberto compartilhada. Feche os descritores salvos e as outras duplicatas quando não precisar mais deles. Esvazie a saída armazenada em buffer antes de alterar ou restaurar STDOUT_FILENO; os buffers de E/S da biblioteca padrão de C são separados das operações de descritor do kernel. Essas verificações tornam previsíveis a substituição e a limpeza dos descritores e evitam suposições sobre seus números.