É possível gravar em outros descritores de arquivos em C?

1

Se eu quiser redirecionar todos os stdout para um arquivo, eu executaria

my_prog 1> out

Se eu quiser fazer o mesmo por stderr, eu corro

my_prog 2> err

No entanto, sei que no shell existem outros descritores de arquivos também. Por exemplo, a resposta principal em esta pergunta usa um terceiro descritor de arquivo , para o qual se pode enviar dados a partir da linha de comando via

echo "Some console message" 1>&3

Existe uma maneira de um programa em C gravar nesse descritor de arquivo? Escrever para ele enquanto nenhum outro programa o está lendo, também envia sua saída para o Terminal?

    
por Alex 11.08.2017 / 21:54

4 respostas

3

Eu não estou realmente familiarizado com C, então pode haver maneiras melhores para isso, mas você pode simplesmente usar o syscall ( man 2 write ):

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);

Onde os dados gravados são enviados depende do que o descodificador de arquivos abriu. Os dados chegam ao terminal somente se o descritor de arquivos pertencer a um terminal.

Se você gravar em um pipeline ou soquete e o outro lado não ler os dados, o aplicativo de gravação (ou seja, a write() call) bloqueará quando os buffers estiverem cheios.

Você pode ver os descritores de arquivo em /proc/$PID/fd

    
por 11.08.2017 / 22:01
6

Para esclarecer mais algumas coisas. Em:

echo foo >&3

echo não está escrevendo "foo\n" em seu descritor de arquivo 3. echo está sempre gravando em stdout, em seu descritor de arquivo 1. Ele está chamando a chamada de sistema write() com um 1 integer como primeiro argumento, um ponteiro para uma área na memória que começa com foo\n como o segundo argumento e 4 (o comprimento de foo\n ) como o terceiro.

Em C, você escreveria write(1, "foo\n", 4) . No código acima, o shell redireciona o fd 1 para a mesma descrição do arquivo aberto como aberto no fd 3 antes de chamar echo (por meio da chamada de sistema dup2() ). Então, mesmo que seja funcionalmente equivalente, não é o mesmo que fazer write(3, "foo\n", 4) . Na verdade, é algo como (simplificado):

if (pid = fork())
  waitpid(pid, ...);
else {
  dup2(3, 1);
  execlp("echo", "foo", 0);
} 

E echo faz um write(1, "foo\n", 4)

Exceto que, em praticamente todos os shells, echo está embutido, por isso não há fork ou exec . Em vez disso, o shell faz:

saved_stdout = dup(1);
dup2(3, 1);
builtin_echo("foo");
dup2(saved_stdout, 1); close(saved_stdout); /* restore stdout */

(onde builtin_echo() é uma função que faz o write(1, "foo\n", 4) no mesmo processo).

Para um comando que faz write(3, "foo\n", 4) , você pode dar uma olhada no comando ksh / zsh print -u3 foo builtin.

Agora, todo processo é gratuito para usar os descritores de arquivos como desejarem. Exceto que 0, 1 e 2 são por convenção reservados para stdin, stdout e stderr. Outros fds geralmente não são especiais, mas em shells (que são apenas um tipo de aplicativo), fds 0 a <some-value> em que algum valor é pelo menos 9 são reservados para uso pelo usuário (do shell). A concha não se misturará com a sua própria sopa interna. Por exemplo, meu saved_stdout = dup(1) acima foi uma aproximação. Com efeito, o shell garantirá que saved_stdout seja um valor maior que <some-value> .

Agora, como não há nenhuma convenção anexada a fds fora de 0,1,2, você não pode esperar que algo esteja aberto no fd 3. O mais provável é que ele seja fechado. Ou se não for, é provável que o chamador do seu script tenha esquecido de fechá-lo (ou de adicionar o O_CLOEXC flag a ele), pois não haveria motivo para deixá-lo aberto para você, pois ninguém espera que nada esteja aberto no fd 3.

Você usaria o fd 3 se soubesse que ele tinha sido aberto em algo, geralmente por você no mesmo script de antemão, como em:

 {
   var=$(cmd 2>&1 >&3)
 } 3>&1

Onde o fd 3 foi definido como dup() de fd 1 antes nós o usamos para cmd to dup() de volta para fd 1.

    
por 12.08.2017 / 08:32
2

Assumindo que o descritor está aberto, C pode, de fato, write (ou mais provavelmente usar alguma chamada de nível mais alto que acabará por se resumir a um write(2) ) para esse descritor. Isso aqui é write2three.c

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    int fd;
    if ((fd = dup(3)) == -1) err(1, "dup failed");
    dprintf(fd, "hello\n");
    exit(EXIT_SUCCESS);
}

Por exemplo,

$ make write2three  
cc     write2three.c   -o write2three
$ ./write2three
write2three: dup failed: Bad file descriptor
$ rm out
$ ./write2three 3>out
$ cat out
hello
$ 

Você pode precisar limpar o shell depois.

$ exec 3>&-
$ 
    
por 11.08.2017 / 22:37
0

Is it possible to write to other file descriptors in C?

Sim.

A maioria dos processos herda 3 descritores de arquivos abertos do pai que os executou.

stdin , stdout e stderr . Estes são os descritores de arquivo 0 - 2, respectivamente.

Eu acredito que o fd 0 ( stdin ) é aberto no modo somente leitura, então seu programa c pode ler este descritor de arquivo mas não escrever nele.

Quando você programa abre outro arquivo, ele obtém o próximo descritor de arquivo em ordem incremental.

    
por 11.08.2017 / 22:02