Como enviar uma string simples entre dois programas usando tubos?

111

Tentei pesquisar na rede, mas quase não há recursos. Um pequeno exemplo seria suficiente.

EDITAR Quero dizer, dois programas C diferentes se comunicando. Um programa deve enviar "Olá" e o outro deve recebê-lo. Algo parecido.


fonte
1
Presumivelmente, você não quer dizer algo como ls | grep ".o"? Talvez um pouco mais de explicação do que você quis dizer ajudasse ...
Jerry Coffin
13
Vamos cara ... um pouco de esforço. Google "código de exemplo de tubos c". O primeiro resultado é exato: tldp.org/LDP/lpg/node11.html
Stephen
4
Eu quero a comunicação entre dois programas completamente diferentes. Não consegui encontrar um recurso para isso.
1
Se você não estiver bifurcando um processo, precisará examinar os "canais nomeados".
Juiz Maygarden

Respostas:

156

Um tubo regular pode conectar apenas dois processos relacionados. Ele é criado por um processo e desaparecerá quando o último processo o fechar.

Um pipe nomeado , também chamado de FIFO por seu comportamento, pode ser usado para conectar dois processos não relacionados e existe independentemente dos processos; o que significa que pode existir mesmo se ninguém o estiver usando. Um FIFO é criado usando a mkfifo()função de biblioteca.

Exemplo

escritor.c

#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

int main()
{
    int fd;
    char * myfifo = "/tmp/myfifo";

    /* create the FIFO (named pipe) */
    mkfifo(myfifo, 0666);

    /* write "Hi" to the FIFO */
    fd = open(myfifo, O_WRONLY);
    write(fd, "Hi", sizeof("Hi"));
    close(fd);

    /* remove the FIFO */
    unlink(myfifo);

    return 0;
}

reader.c

#include <fcntl.h>
#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

#define MAX_BUF 1024

int main()
{
    int fd;
    char * myfifo = "/tmp/myfifo";
    char buf[MAX_BUF];

    /* open, read, and display the message from the FIFO */
    fd = open(myfifo, O_RDONLY);
    read(fd, buf, MAX_BUF);
    printf("Received: %s\n", buf);
    close(fd);

    return 0;
}

Nota: A verificação de erros foi omitida do código acima para simplificar.

jschmier
fonte
6
O que são considerados processos relacionados ?
Pithikos de
7
Provavelmente processos relacionados por meio de uma ou mais relações pai / filho (por exemplo, inclui irmãos). O ancestral comum teria criado as duas extremidades do tubo. Processos não relacionados carecem desse ancestral comum.
MSalters
4
Isso não funcionará se o leitor começar primeiro. Uma solução rápida seria colocar o open()do leitor dentro de um loop. No entanto, +1 porque você fornece um exemplo de dois programas.
gsamaras de
Presumo que este exemplo precisa de alguns ajustes para funcionar no Windows? unistd.h sendo POSIX e tudo ...
David Karlsson
Sim, vai precisar de ajustes para o Windows. O artigo da Wikipedia sobre pipes nomeados discute algumas das diferenças Unix / Windows e uma rápida pesquisa no Google pode ajudar na implementação do Windows.
jschmier
41

Em Criando tubos em C , mostra como bifurcar um programa para usar um tubo. Se você não quiser fazer um fork (), pode usar canais nomeados .

Além disso, você pode obter o efeito de prog1 | prog2enviando a saída de prog1para stdout e lendo de stdindentro prog2. Você também pode ler stdin abrindo um arquivo chamado /dev/stdin(mas não tenho certeza da portabilidade disso).

/*****************************************************************************
 Excerpt from "Linux Programmer's Guide - Chapter 6"
 (C)opyright 1994-1995, Scott Burkett
 ***************************************************************************** 
 MODULE: pipe.c
 *****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>

int main(void)
{
        int     fd[2], nbytes;
        pid_t   childpid;
        char    string[] = "Hello, world!\n";
        char    readbuffer[80];

        pipe(fd);

        if((childpid = fork()) == -1)
        {
                perror("fork");
                exit(1);
        }

        if(childpid == 0)
        {
                /* Child process closes up input side of pipe */
                close(fd[0]);

                /* Send "string" through the output side of pipe */
                write(fd[1], string, (strlen(string)+1));
                exit(0);
        }
        else
        {
                /* Parent process closes up output side of pipe */
                close(fd[1]);

                /* Read in a string from the pipe */
                nbytes = read(fd[0], readbuffer, sizeof(readbuffer));
                printf("Received string: %s", readbuffer);
        }

        return(0);
}
Stephen
fonte
1
Ei Stephen, mesmo assim posso usar este código para duas funções diferentes? o que significa que a escrita no pipe é feita em uma função e a leitura do pipe em outra função ?? um código funcional como este seria apreciado.
Mohsin,
8
dup2( STDIN_FILENO, newfd )

E leia:

char reading[ 1025 ];
int fdin = 0, r_control;
if( dup2( STDIN_FILENO, fdin ) < 0 ){
    perror( "dup2(  )" );
    exit( errno );
}
memset( reading, '\0', 1025 );
while( ( r_control = read( fdin, reading, 1024 ) ) > 0 ){
    printf( "<%s>", reading );
    memset( reading, '\0', 1025 );
}
if( r_control < 0 )
    perror( "read(  )" );    
close( fdin );    

Mas, acho que fcntlpode ser uma solução melhor

echo "salut" | code
mlouk
fonte
6

O que um programa grava em stdout pode ser lido por outro via stdin. Então, simplesmente, usando c, escreva prog1para imprimir algo usando printf()e prog2para ler algo usando scanf(). Então é só correr

./prog1 | ./prog2
João
fonte
4

Aqui está um exemplo :

int main()
{
    char buff[1024] = {0};
    FILE* cvt;
    int status;
    /* Launch converter and open a pipe through which the parent will write to it */
    cvt = popen("converter", "w");
    if (!cvt)
    {
        printf("couldn't open a pipe; quitting\n");
        exit(1)
    }
    printf("enter Fahrenheit degrees: " );
    fgets(buff, sizeof (buff), stdin); /*read user's input */
    /* Send expression to converter for evaluation */
    fprintf(cvt, "%s\n", buff);
    fflush(cvt);
    /* Close pipe to converter and wait for it to exit */
    status=pclose(cvt);
    /* Check the exit status of pclose() */
    if (!WIFEXITED(status))
        printf("error on closing the pipe\n");
    return 0;
}

As etapas importantes neste programa são:

  1. A popen()chamada que estabelece a associação entre um processo filho e um pipe no pai.
  2. A fprintf()chamada que usa o pipe como um arquivo comum para gravar no stdin do processo filho ou ler em seu stdout.
  3. A pclose()chamada que fecha o pipe e faz com que o processo filho seja encerrado.
Preet Sangha
fonte
Eu acho que este exemplo perde o ponto da questão, embora eu admita que o programa "conversor" seja um programa diferente. O primeiro comentário aborda a comunicação entre programas completamente independentes que não têm uma relação irmão / pai / primo em segundo grau.
cmm
2

Primeiro, faça com que o programa 1 escreva a string stdout(como se você gostaria que ela aparecesse na tela). Em seguida, o segundo programa deve ler uma string de stdin, como se um usuário estivesse digitando em um teclado. então você executa:

$ program_1 | program_2
Ifagundes
fonte
1

Esta resposta pode ser útil para um futuro Googler.

#include <stdio.h>
#include <unistd.h>

int main(){     
     int p, f;  
     int rw_setup[2];   
     char message[20];      
     p = pipe(rw_setup);    
     if(p < 0){         
        printf("An error occured. Could not create the pipe.");  
        _exit(1);   
     }      
     f = fork();    
     if(f > 0){
        write(rw_setup[1], "Hi from Parent", 15);    
     }  
     else if(f == 0){       
        read(rw_setup[0],message,15);       
        printf("%s %d\n", message, r_return);   
     }  
     else{      
        printf("Could not create the child process");   
     }      
     return 0;

}

Você pode encontrar um exemplo avançado de chamada de tubo bidirecional aqui .

Anjana
fonte