Почему дочерний процесс возвращает статус выхода = 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
в отдельной оболочке.