Restaurar stdout

1. Restaurar stdout

???
gokernel

(usa Linux Mint)

Enviado em 07/11/2016 - 06:53h

Olá !

Estou redirecionando a saida ( stdout ), mas não estou conseguindo restaurar essa.

Vou direto:
Como restaurar ( stdout ) ?


#include <stdio.h>

int main ()
{
FILE *fp;

printf("escreve na tela: stdout\n");

fp = freopen("file.txt", "w", stdout);

printf("AGORA ESSE, escreve no arquivo file.txt\n");

fclose(stdout);

// aqui da erro, mas se retirar o retorno ( stdout ) funciona
stdout = fdopen(1, "w");

printf ("Testando se foi restaurado agora\n");

return(0);
}



  


2. Re: Restaurar stdout

Paulo
paulo1205

(usa Ubuntu)

Enviado em 07/11/2016 - 17:27h

O descritor de arquivo usado internamente pelo stream stdout provavelmente não está sendo preservado depois que você faz o freopen().

Para confirmar a hipótese, eu fiz o seguinte programa.

#include <stdio.h>

int main(void){
fprintf(stderr, "%p\n", freopen("/dev/null", "w", stdout));
}
.

Ao executá-lo sob o strace, eis o que ele mostrou (na parte relevante).

open("/dev/null", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3
dup3(3, 1, 0) = 1
close(3) = 0


Ou seja: o arquivo foi aberto com um descritor novo (3), o descritor sobrescreveu o antigo (1), e o descritor novo foi fechado. Logo não existe mais referência ao descritor original, ligado ao terminal.

O que você pode fazer é usar dup() ou dup2() antes de chamar freopen(), salvando o descritor original em algum outro, e depois restaurar isso para stdout.

int stdout_fd_copy;
If(
(stdout_fd_copy=dup(fileno(stdout)))==-1 ||
freopen("/tmp/out.txt", "w", stdout)==NULL
){
fprintf(stderr, "Unable to redirect: %s\n", strerror(errno);
exit(1);
}

/* ... Usa redirecionamento... */

// Restaura stdout original “na unha”.
if(
fflush(stdout)!=0 ||
dup2(stdout_fd_copy, fileno(stdout))==-1 ||
close(stdout_fd_copy)==-1
){
fprintf(stderr, "Unable to restore original stdout: %s\n", strerror(errno));
exit(1);
}




3. Re: Restaurar stdout

???
gokernel

(usa Linux Mint)

Enviado em 08/11/2016 - 06:17h

Olá "paulo1205", grato pela resposta !!!

Mas modifiquei o programa ... reenviando ( stdout ) para um string:

Parte do código em teoria:

char stdout_string[3000];
int i;

int main()
{
// inicia normal...
setvbuf (stdout, stdout_string, _IOFBF, sizeof(stdout_string));

// bla, bla, bla
// ... faz muitas coisas
// ... excutando MUITAS COISAS AQUI ...



//---------------------------------------
// reset
setbuf (stdout, NULL);

// zera o string ;)
//
for (i = 0; i < sizeof(stdout_string); i++)
stdout_string[i] = 0;

// redefine
setvbuf (stdout, stdout_string, _IOFBF, sizeof(stdout_string));
//---------------------------------------


// continua utilizando "MUITAS COISAS"
// ...
// ...


return 0;
}



Agora no "meu programa aqui" a saida está redirecionada para ( stdout_string ) ... e está funcionando "como uma gambiarra" ;).

Estou ainda implementando melhor ...



4. Re: Restaurar stdout

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/11/2016 - 09:50h

Na resposta anterior eu procurei responder sua dúvida de modo técnico, sem entrar no mérito de adequação. Agora, porém, vou me estender um pouquinho mais sobre contexto e sobre efeitos.

Que mal lhe pergunte: seu programa chama comandos externos que dependam do redirecionamento?

Porque, se não chamar, não faz sentido redirecionar. É melhor você simplesmente gerar a saída com fprintf(arquivo, "blablabla" ...) em vez de redirecionar stdout para arquivo e chamar printf("blablabla" ...), e depois desfazer o redirecionamento.

O único caso que eu consigo imaginar que talvez justifique um redirecionamento seria se você já tiver um código muito extenso que tenha sido originalmente escrito para gerar saída na tela, e você agora desejar que a saída vá para um arquivo. Mas, mesmo nesse caso, eu consideraria seriamente modificar o código original, possivelmente com ajuda de uma ferramenta automatizada de substituição de texto, com vistas a deixar o comportamento de saída para arquivo evidente no código, a fim de não surpreender um eventual leitor do código que você criar com um comportamento não-padrão, forçado e controlado por uma parte do código que pode estar distante da que ele estiver examinando.

Se, por outro lado, você chamar programas externos (ou mesmo se existir a possibilidade de vir a chamá-los no futuro), sua abordagem de fazer através de buffers simplesmente não funcionará. Seu buffer só vale dentro do processo em que foi criado, e outros processos não terão acesso a essa memória para escrever nela.

Aliás, o problema não ocorre somente quando você chama programas externos. Um simples fork() já será suficiente para que processos diferentes tenham buffers diferentes.


5. Re: Restaurar stdout

???
gokernel

(usa Linux Mint)

Enviado em 08/11/2016 - 15:19h

Sim o programa depende de comandos externos.

Sendo direto: o programa é um compilador/ambiente "rodando em uma GUI" tambem com possibilidade em usar popen para executar programas externos.

E esta funcionando legal... "gambiarra lembra" ? ;)



6. Re: Restaurar stdout

???
gokernel

(usa Linux Mint)

Enviado em 08/11/2016 - 17:39h


Aproveitando a oportunidade, por aqui passa algumas pessoas perguntando como redirecionar ( stdout ) ...

Fiz um pequeno exemplo usando SDL 1.x, pois assim fica fácil para entender.

Aqui:
http://codepad.org/aZUn28i6




7. Re: Restaurar stdout

Paulo
paulo1205

(usa Ubuntu)

Enviado em 08/11/2016 - 18:26h

Caro Gokernel,

Com todo respeito, isso é um exemplo de como NÃO se deve programar.

O programa que você mostrou por último simplesmente reinventa snprintf(), só que de modo piorado.

Ainda por cima, é ERRADO mexer com buffers de um stream ativo, como você fez no final do seu programa. Veja o que diz a documentação do Linux.

The setvbuf() function may be used only after opening a stream and before any other operations have been performed on it.


A documentação do POSIX diz a mesma coisa, mas com outra linguagem.

The setvbuf() function may be used after the stream pointed to by stream is associated with an open file but before any other operation (other than an unsuccessful call to setvbuf()) is performed on the stream.


EDIT: Inicialmente eu tinha enxergado o erro apenas no final do programa, quando você chama setbuf() (que é uma macro em torno de setvbuf()), mas a coisa é errada desde o início. Quando você usa stdout, o aquivo já chega para você aberto e com uma chamada bem-sucedida a setvbuf(), que associa a ele um buffer orientado a linhas. Logo você viola de cara uma cláusula explícita na documentação do POSIX, presente também na documentação do Linux, embora menos claramente, quando fala em “antes que qualquer outra operação tenha sido realizada”.

----

Se você realmente precisar de redirecionamento da saída padrão, uma forma muito mais segura de trabalhar é redirecionando realmente o descritor associado à saída padrão, seja para um arquivo, para um pipe ou para um socket, num processo separado, e então fazer as operações de saída desse processo normalmente.

Do contrário, existem soluções padronizadas e que não recorrem a gambiarras e práticas questionáveis para preencher devidamente uma string com texto formatado. Sugiro que você use uma delas.


8. Re: Restaurar stdout

???
gokernel

(usa Linux Mint)

Enviado em 08/11/2016 - 19:06h

Ok paulo anotado. ;)







Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts