Почему дочерний процесс возвращает статус выхода = 32512 в unix?

В моей программе я выполняю заданную команду и получаю результат (журнал и статус выхода). Также моя программа должна поддерживать специальные команды оболочки (то есть команды, которые содержат специальные символы оболочки ~ (tild), | (pipe), *). Но когда я пытаюсь запустить sh -c ls | wc в своем домашнем каталоге через мою программу, он завершился неудачно, и его статус выхода был 32512, также был напечатан поток stderr "sh: ls | wc: command not found".

Но интересно то, что команда sh -c ls | wc работает корректно, если я запускаю ее в оболочке.

В чем проблема? Или более предпочтительным, как я могу запускать специальные команды с помощью моей программы (i.ec какая команда, с какими параметрами я должен работать)?

Ниже приведена часть кода в дочерней части после fork(). Он выполняет команду.

tokenized_command есть std::vector<std::string>, где в моем случае "sh", "-c", "ls", "|", "wc" сохраняются, также я попытался сохранить там "sh", "-c", "\"ls | wc\"", но результат будет таким же. command char *, где сохраняется полная командная строка.

        boost::shared_array<const char *> bargv(new const char *[tokenized_command.size() + 1]);
        const char **argv = bargv.get();
        for(int i = 0; i < tokenized_command.size(); ++i)
        {
            argv[i] = tokenized_command[i].c_str();
            printf("argv[%d]: %s\n", i, argv[i]); //trace
        }
        argv[tokenized_command.size()] = NULL;

        if(execvp(argv[0], (char * const *)argv) == -1)
        {
            fprintf(stderr, "Failed to execute command %s: %s", command, strerror(errno));
            _exit(EXIT_FAILURE);
        }

P.S.

Я знаю, что использование system(command) вместо execvp может решить мою проблему. Но system() ждет, пока команда не будет завершена, и это недостаточно для моей программы. И также я уверен, что в реализации system() используется одна из функций exec-family, поэтому проблема может быть решена и с помощью exec, но я не знаю, как это сделать.

Ответ 1

execvp принимает путь к исполняемому файлу и аргументы, с помощью которых можно запустить этот исполняемый файл. Он не принимает команды оболочки bourne.

ls | wc - это команда оболочки bourne (среди прочего), и она не может быть разбита на путь к исполняемому файлу и некоторые аргументы из-за использования канала. Это означает, что он не может быть выполнен с помощью execvp.

Чтобы выполнить команду оболочки bourne с помощью execvp, нужно выполнить sh и передать -c и команду для аргументов.

Итак, вы хотите выполнить ls | wc с помощью execvp.

char *const argv[] = {
    "sh",
    "-c", "ls | wc",  // Command to execute.
    NULL
};

execvp(argv[0], argv)

Вы, видимо, пытались

char *const argv[] = {
    "sh",
    "-c", "ls",  // Command to execute.
    "|",         // Stored in called sh $0.
    "wc",        // Stored in called sh $1.
    NULL
};

Это будет то же самое, что и команда bourne shell sh -c ls '|' wc.

И оба они сильно отличаются от команды оболочки sh -c ls | wc. Это будет

char *const argv[] = {
    "sh",
    "-c", "sh -c ls | wc",  // Command to execute.
    NULL
};

Кажется, вы думаете, что | и wc передаются в sh, но это совсем не так. | - это особый символ, который приводит к трубе, а не к аргументу.


Что касается кода выхода,

Bits 15-8 = Exit code.
Bit     7 = 1 if a core dump was produced.
Bits  6-0 = Signal number that killed the process.

32512 = 0x7F00

Таким образом, он не умер от сигнала, дамп ядра не был создан, и он вышел с кодом 127 (0x7F).

Что означает 127, неясно, поэтому оно должно сопровождаться сообщением об ошибке. Вы пытались выполнить программу ls | wc, но такой программы нет.

Ответ 2

Вы должны выполнить sh -c 'ls | wc'.

Опция -c ожидает команду в виде строки. В оболочке, конечно, она работает, потому что нет разницы между нерестом ls и перенаправлением вывода на wc и запуском ls | wc в отдельной оболочке.