Tworzenie potoku jak w bashu

Tworzenie potoku jak w bashu
PA
  • Rejestracja:ponad 6 lat
  • Ostatnio:4 dni
  • Postów:31
1

Witam,

Zastanawiałem się jak działa tworzenie potoku w bashu, np:

Kopiuj
sleep 3600 | cat | sort -u | cut -d' ' -f1

Tutaj oczywiście nic się nie przenosi na kolejne programy (sleep nic nie wypisuje, tylko czeka), ale mi będzie chodziło o zasadę tworzenia takiego potoku w C. Używając polecenia ps razem z tym wyżej dostaję taki wynik:

Kopiuj
sleep 3600 | cat | sort -u | cut -d' ' -f1 & ps -Hp $$ --ppid $$ -o pid,pgid,ppid,sid,tname,comm
Kopiuj
  PID    PGID    PPID     SID TTY      COMMAND
67855   67855    5971   67855 pts/0    bash
73274   73274   67855   67855 pts/0      sleep
73275   73274   67855   67855 pts/0      cat
73276   73274   67855   67855 pts/0      sort
73277   73274   67855   67855 pts/0      cut
73278   73278   67855   67855 pts/0      ps

Otwarte deskryptory plików dla każdego programu:

Kopiuj
for PID in `ps --ppid $$ -o pid=` ; do cat /proc/$PID/comm 2>/dev/null ; ls -l /proc/$PID/fd 2>/dev/null ; echo ; done
Kopiuj
sleep
razem 0
lrwx------ 1 admin admin 64 10-18 22:23 0 -> /dev/pts/0
l-wx------ 1 admin admin 64 10-18 22:23 1 -> 'pipe:[38322102]'
lrwx------ 1 admin admin 64 10-18 22:23 2 -> /dev/pts/0

cat
razem 0
lr-x------ 1 admin admin 64 10-18 22:23 0 -> 'pipe:[38322102]'
l-wx------ 1 admin admin 64 10-18 22:23 1 -> 'pipe:[38322104]'
lrwx------ 1 admin admin 64 10-18 22:23 2 -> /dev/pts/0

sort
razem 0
lr-x------ 1 admin admin 64 10-18 22:23 0 -> 'pipe:[38322104]'
l-wx------ 1 admin admin 64 10-18 22:23 1 -> 'pipe:[38322105]'
lrwx------ 1 admin admin 64 10-18 22:23 2 -> /dev/pts/0

cut
razem 0
lr-x------ 1 admin admin 64 10-18 22:23 0 -> 'pipe:[38322105]'
lrwx------ 1 admin admin 64 10-18 22:23 1 -> /dev/pts/0
lrwx------ 1 admin admin 64 10-18 22:23 2 -> /dev/pts/0

Analizując to stwierdziłem, że bash tworzy pipe dla każdego wystąpienia znaku "|", ponadto ustawia PGID dla każdego następnego procesu na PID pierwszego procesu w potoku. Tak więc, czy takie coś w C odzwierciedla to, co bash robi? Czy może czegoś nie zauważyłem, co jeszcze bash robi w przypadku potoków?

Kopiuj
const char * const (* const cmd[])[] = {
    &(const char * const[]){"sleep", "3600", NULL},
    &(const char * const[]){"cat", NULL},
    &(const char * const[]){"sort", "-u", NULL},
    &(const char * const[]){"cut", "-d ", "-f1", NULL},
    NULL
};

struct {
    int next_read;
    int write;
    int read;
} pipes;

int pid, pgid = 0, index = 0;

while (cmd[index] != NULL) {
    if (cmd[index + 1] != NULL)
        pipe((int*)&pipes);

    if (!(pid = fork())) {
        setpgid(0, pgid);

        if (index > 0) {
            dup2(pipes.read, 0);
            close(pipes.read);
        }

        if (cmd[index + 1] != NULL) {
            close(pipes.next_read);
            dup2(pipes.write, 1);
            close(pipes.write);
        }

        execvp((*cmd[index])[0], (char * const *)cmd[index]);
    }

    if (index > 0)
        close(pipes.read);
    else
        pgid = pid;

    if (cmd[index + 1] != NULL) {
        pipes.read = pipes.next_read;
        close(pipes.write);
    }

    ++index;
}
Riddle
@pajacol: Ale ładne formatowanie! Najs.
overcq
  • Rejestracja:około 7 lat
  • Ostatnio:około 5 godzin
  • Postów:393
0

Z tego, co widzę w źródłach ‟bash”, to zawiera on szereg procedur “execute_*” wykonywanych po zinterpretowaniu określonej konstrukcji wiersza poleceń. Na przykład w procedurze “execute_connection”.
Same ‘pipes’ są przekierowywane w “do_piping”. I widać, że jest jeszcze jeden przypadek, którego nie uwzględniłeś: przekierowanie zarówno standardowego wyjścia jak i wyjścia błędów.
Ponadto nie obsługujesz błędów “fork”, “dup2”, “execvp”.
Twój kod wygląda znacznie czytelniej niż źródła “bash”. ;)


Nie znam się, ale się wypowiem.
Wizytówka
joh­nny_Be_go­od jest mistrzem ‘eskejpowania’ i osadzania.
KS
  • Rejestracja:prawie 4 lata
  • Ostatnio:22 minuty
  • Postów:623
0

Hejka tak na boku - wydaje mi się, ze macierz nie może zawierać pointerów do macierzy które mają różne wielkości. Czyli tam ta kolejna powinna być statycznie podana największa po prostu. Nie pasuje mi to ale nie chcę mówić, na sto procent, że jest źle. Potem zasięgnę języka.
Ja bym tak zrobił:

Kopiuj
const char *const *const ( cmd[]) = {
        (const char *const []){"sleep", "3600", NULL},
        (const char *const []){"cat", NULL},
        (const char *const []){"sort", "-u", NULL},
        (const char *const []){"cut", "-d ", "-f1", NULL},
        NULL
};

Co do działąnia to fork + execve i pipe z jednego do kolejnego. stdout trafia na stdin


EDIT,
wygląda, że nie mam racji ze swoją pierwszą częścią o macierzach. Jutro więcej napiszę.

EDIT2:
Nie no jest spoko, wolno tak robić. Moje argusowe oko było zbyt czujne!

edytowany 2x, ostatnio: ksh

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.