Передайте две или более команд оболочки в C, используя цикл - PullRequest
0 голосов
/ 22 марта 2020

Я пытаюсь выполнить ls | wc -l через программу в C вместо использования командной строки. Это мой текущий рабочий код:

int main() { 
   int pfds[2]; 
   pipe(pfds); 
   pid_t pid = fork(); 
   if ( pid == 0 ) { /* The child process*/ 
      close(1);
      dup(pfds[1]);
      close(pfds[0]);
      execlp("ls", "ls", NULL); 
   } else { /* The parent process*/ 
      close(0);
      dup(pfds[0]);
      close(pfds[1]);
      wait(0);
      execlp("wc", "wc", "-l", NULL); 
   } 
   return 0; 
}

Как бы я переписал этот код для работы с for-l oop?

Например:

for (i=0; i<2; i++) {

    // Rewrite the 2-level pipe here

}

Позже я хотел бы расширить для l oop, чтобы выполнить больше процессов, переданных по конвейеру, как a | b | c | ...

1 Ответ

1 голос
/ 23 марта 2020

Чтобы соединить несколько команд вместе, вам нужно, чтобы родительский узел работал для каждой команды fork().

Используя for l oop, вам необходимо сделать это для первых n - 1 команд (последняя будет выполнена в основной программе):

  1. Создать канал.
  2. Выполнить fork().
  3. В дочернем: перезаписать стандартный ввод с конца чтения предыдущего канала и стандартный вывод с записью конца текущего канала.
  4. В дочернем: execute execve() .
  5. В родительском объекте: закрыть ненужные каналы и сохранить конец чтения текущей трубы, который будет использоваться в следующем цикле.

Затем, после окончания l oop, перезаписать стандартный ввод с конца чтения последнего канала и выполнения execve() последней команды.


Ниже я написал простой рабочий пример, который выполняет:

ls | wc -l | xargs printf "0x%x\n" | cowsay

Это должно работать для любого количества команд (включая только одну отдельную команду).

ПРИМЕЧАНИЕ : I не добавлял проверки ошибок в этом коде отдельно для execvp() только для того, чтобы сделать его коротким, но вы обязательно должны проверять ошибки после каждого вызова pipe(), dup2(), fork() и любой другой функции.

Код:

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

#define MAX_ARGC 3

int main(void) {
    char *commands[][MAX_ARGC + 1] = {
        {"ls", NULL},
        {"wc", "-l", NULL},
        {"xargs", "printf", "0x%x\n", NULL},
        {"cowsay", NULL}
    };

    size_t i, n;
    int prev_pipe, pfds[2];

    n = sizeof(commands) / sizeof(*commands);
    prev_pipe = STDIN_FILENO;

    for (i = 0; i < n - 1; i++) {
        pipe(pfds);

        if (fork() == 0) {
            // Redirect previous pipe to stdin
            if (prev_pipe != STDIN_FILENO) {
                dup2(prev_pipe, STDIN_FILENO);
                close(prev_pipe);
            }

            // Redirect stdout to current pipe
            dup2(pfds[1], STDOUT_FILENO);
            close(pfds[1]);

            // Start command
            execvp(commands[i][0], commands[i]);

            perror("execvp failed");
            exit(1);
        }

        // Close read end of previous pipe (not needed in the parent)
        close(prev_pipe);

        // Close write end of current pipe (not needed in the parent)
        close(pfds[1]);

        // Save read end of current pipe to use in next cycle
        prev_pipe = pfds[0];
    }

    // Get stdin from last pipe
    if (prev_pipe != STDIN_FILENO) {
        dup2(prev_pipe, STDIN_FILENO);
        close(prev_pipe);
    }

    // Start last command
    execvp(commands[i][0], commands[i]);

    perror("execvp failed");
    exit(1);
}

Вывод на мою машину (поскольку ls вернул 41 == 0x29 строк):

 ______
< 0x29 >
 ------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||
...