Как объединить команды `lshw` и` grep` внутри `execv` в C? - PullRequest
0 голосов
/ 29 апреля 2020

Вот программа C, которая выполняет поиск определенных свойств c, таких как информация шины ЦП, с помощью последовательных вызовов lshw (для доступа к общему списку оборудования с соответствующими свойствами) и grep (для выбора только соответствующей точки среди lshw результатов):

char *strCombine(char *str1, char *str2, int n)
{
    int i = strlen(str2);
    int j = 0;
    if((str2 = (char *) realloc(str2, (i + n + 1))) == NULL)
        perror(0);
    while(j < n && str1[j])
    {
        str2[i] = str1[j];
        i++;
        j++;
    }
    str2[i] = 0;
    return (str2);
}

int main() 
{
    pid_t parent;
    char buf[1000] = {0};
    char *str;
    char *argv[6] = {"/usr/bin/lshw", "-C", "CPU", "|", "grep", "bus info"};       
    int fd[2];
    int ret;

    if(pipe(fd) == -1)
    {
        perror(NULL);
        return -1;
    }
    parent = fork();
    if(parent == 0)
    {
        close(fd[1]);
        while((ret = read(fd[0], buf, 1000)))
            str = strCombine(buf, str, ret);
        close(fd[0]);
    }
    else
    {
        close(fd[0]);
        execv(argv[0], argv);            
        close(fd[1]);
        wait(0);
    }
    wait(0);
    printf("%s", str);


    return 0;
}

В этом коде ожидается, что grep будет следовать lshw, поскольку оба go выполняются при вызове execv. Однако этот конвейер не работает, потому что ссылка на использование lshw распечатывается в терминале (работает на Ubuntu 18.04 LTS) вместо изначально необходимого bus info. Что заставляет эту программу не показывать только информацию, которая имеет значение, и каким образом я должен пытаться настроить конвейер?

1 Ответ

0 голосов
/ 04 мая 2020

Вертикальная черта не является параметром, который вы используете для разделения команд, поскольку системный вызов execve(2) загрузит программу в виртуальное пространство только одного процесса . Вам необходимо создать два процесса, по одному на каждую команду, которую вы хотите выполнить, и передать их так, чтобы входные данные от одного перешли к выходным для другого. Я также думаю, что вы будете заинтересованы в выводе последней команды, поэтому вам нужно сделать два перенаправления (одно от первой команды ко второй и одно от вывода второй команды в дескриптор канала), две вилки и два exe c для того, чтобы сделать это.

Сначала хорошие новости, вы можете делать все эти вещи с помощью простого вызова popen(3) без мимолетных мелочей делать вилки и execs, пока перенаправление ввода / вывода из отдельных команд. Просто используйте это:

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

int main()
{
        char *cmd = "/usr/bin/lshw -C CPU | grep 'bus info'";
        int n = 0;
        char line[1000];

        /* f will be associated to the output of the pipeline, so you can read from it.
         * this is stated by the "r" of the second parameter */
        FILE *f = popen(cmd, "r");
        if (!f) {
                perror(cmd);
                exit(EXIT_FAILURE);
        }
        /* I read, line by line, and process the output,
         * printing each line with some format string, but
         * you are free here. */
        while (fgets(line, sizeof line, f)) {
                char *l = strtok(line, "\n");
                if (!l) continue;
                printf("line %d: [%s]\n", ++n, l);
        }

        /* once finished, you need to pclose(3) it.  This
         * makes program to wait(2) for child to finish and
         * closing descriptor */
        pclose(f);
}

Если вам нужно смонтировать такой конвейер, вам не придется перенаправлять с первой команды на вторую, со второго на родительский процесс, и fork / exe c оба процесса сами .

В этом подходе вы обрабатываете подоболочку, чтобы выполнить работу по конвейерной обработке и перенаправлению, и просто получаете дескриптор FILE * для чтения.

(если я найду время Я покажу вам полный пример цепочки из N команд с перенаправлениями для их передачи, но я не могу обещать, так как должен написать код)

NOTE

fork() возвращает pid дочернего процесса родителю, а 0 - самому дочернему процессу. Я не понимаю, почему у вас есть переменная с именем parent, в которой хранится значение, полученное от fork(). Если он ненулевой (и неотрицательный), он представляет pid дочернего процесса. Вам нужно два, так как вам нужно два процесса. В примере, который я публикую, вы создаете три процесса (вы просите подоболочку смонтировать конвейер для вас, поэтому у вас есть подоболочка, которой вы поручаете создать еще два процесса для выполнения вашей команды). Если вам нужно было смонтировать все эти атрибуты, вы Я также хотел бы wait(2) для детей до sh (это делается в pclose(3) вызове)

У меня есть небольшая программа для многократного порождения процесса (только один), в то же время печатая его вывод в том же месте. Я использую его как некую программу htop, когда пытаюсь увидеть, например, вывод ls -l (показывающий растущий файл по мере его заполнения) или вывод команды df. Она запускает программу, создает одну ветвь, перенаправляет вывод ее в канал и получает вывод команды для подсчета количества выводимых строк (для выдачи escape-последовательности, чтобы поместить курсор поверх списка, и для вывода очищается до конца строки после каждой выходной строки, поэтому более короткие строки не стираются при более длинных. В нем показано, как обращаться с вилками и системными вызовами exe c, и вы можете использовать в качестве примера о том, как делать все смело. Но наличие popen(3) Я думаю, что это решение вашей проблемы. Если вы хотите взглянуть на мою cont программу, просто найдите ее здесь .

...