Jak wysłać prosty ciąg między dwoma programami za pomocą potoków?

111

Próbowałem szukać w sieci, ale prawie nie ma żadnych zasobów. Wystarczyłby mały przykład.

EDYCJA Mam na myśli dwa różne programy w C komunikujące się ze sobą. Jeden program powinien wysłać „Cześć”, a drugi powinien je odebrać. Coś w tym stylu.


źródło
1
Prawdopodobnie nie masz na myśli czegoś takiego ls | grep ".o"? Być może trochę więcej wyjaśnienia tego, co masz na myśli, pomogłoby ...
Jerry Coffin
13
Chodź stary ... trochę wysiłku. Google „c pipe przykład kod”. Pierwszy wynik jest dokładny: tldp.org/LDP/lpg/node11.html
Stephen
4
Chcę komunikacji między dwoma zupełnie różnymi programami. Nie mogłem znaleźć odpowiedniego źródła.
1
Jeśli nie rozwidlasz procesu, musisz spojrzeć na „nazwane potoki”.
Sędzia Maygarden

Odpowiedzi:

156

Zwykły potok może łączyć tylko dwa powiązane procesy. Jest tworzony przez proces i zniknie, gdy ostatni proces go zamknie.

Nazwie rura , zwana także FIFO jego zachowania może być stosowany do łączenia dwóch niepowiązanych procesów i występuje niezależnie od procesów; co oznacza, że ​​może istnieć, nawet jeśli nikt go nie używa. FIFO jest tworzone za pomocą mkfifo()funkcji biblioteki.

Przykład

writer.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;
}

czytelnik 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;
}

Uwaga: sprawdzanie błędów zostało pominięte w powyższym kodzie dla uproszczenia.

jschmier
źródło
6
Co jest uważane za powiązane procesy ?
Pithikos
7
Prawdopodobnie procesy, które są powiązane przez jedną lub więcej relacji rodzic / dziecko (np. Obejmuje rodzeństwo). Wspólny przodek stworzyłby dwa końce rury. Niepowiązane procesy nie mają tego wspólnego przodka.
MSalters
4
To nie zadziała, jeśli czytnik uruchomi się pierwszy. Szybkim rozwiązaniem byłoby umieszczenie open()czytnika w pętli. Jednak +1, ponieważ podajesz przykład dwóch programów.
gsamaras
Rozumiem, że ten przykład wymaga pewnych poprawek, aby działać w systemie Windows? unistd.h jest POSIX i wszystko ...
David Karlsson
Tak, będzie wymagać poprawek w systemie Windows. Artykuł Wikipedii dotyczący nazwanych potoków omawia niektóre różnice między systemami Unix / Windows, a szybkie wyszukiwanie w Google może pomóc w implementacji systemu Windows.
jschmier
41

Z tworzenia potoków w C , to pokazuje, jak rozwidlić program, aby używał potoku. Jeśli nie chcesz rozwidlać (), możesz użyć nazwanych potoków .

Ponadto, można uzyskać efekt prog1 | prog2wysyłając wyjście prog1do stdout i odczyt z stdinw prog2. Możesz również odczytać stdin, otwierając plik o nazwie /dev/stdin(ale nie jesteś pewien, czy można go przenosić).

/*****************************************************************************
 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
źródło
1
Hej Stephen, w każdym razie mogę użyć tego kodu do dwóch różnych funkcji? czyli pisanie do potoku odbywa się w jednej funkcji, a odczytywanie potoku w innej funkcji? taki działający kod byłby mile widziany.
Mohsin,
8
dup2( STDIN_FILENO, newfd )

I czytać:

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 );    

Ale myślę, że to fcntlmoże być lepsze rozwiązanie

echo "salut" | code
mlouk
źródło
6

To, co jeden program zapisuje na standardowe wyjście, może zostać odczytane przez inny program poprzez stdin. Więc po prostu, używając c, napisz, prog1aby wydrukować coś za pomocą printf()i prog2przeczytać coś za pomocą scanf(). Potem po prostu biegnij

./prog1 | ./prog2
Johan
źródło
4

Oto próbka :

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;
}

Ważne kroki w tym programie to:

  1. popen()Połączenia, które ustala związek między proces potomny i rury w rodzica.
  2. fprintf()Wezwanie, które wykorzystuje rurę jako zwykły plik napisać do stdin procesu potomnego lub odczytać z jego standardowe wyjście.
  3. pclose()Połączenia, które zamyka rurę i powoduje, że proces potomnego.
Preet Sangha
źródło
Myślę, że ten przykład mija się z celem pytania, chociaż przyznaję, że program „konwerter” to inny program. Pierwszy komentarz dotyczy komunikacji między całkowicie niezależnymi programami, które nie mają relacji rodzeństwo / rodzic / drugi kuzyn.
cmm
2

Najpierw stdoutniech program 1 zapisze łańcuch do (tak jakbyś chciał, żeby pojawił się na ekranie). Następnie drugi program powinien odczytać ciąg znaków stdin, tak jakby użytkownik pisał z klawiatury. wtedy biegniesz:

$ program_1 | program_2
lfagundes
źródło
1

Ta odpowiedź może być pomocna dla przyszłego pracownika Google.

#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;

}

Można znaleźć zaawansowaną dwukierunkową przykład połączenia rura tutaj .

Anjana
źródło