функция exec только запускает некоторые команды, не запускает echo - PullRequest
0 голосов
/ 11 мая 2019

Я пытаюсь запустить аргументы командной строки (особенно echo) через семейство функций exec.Я могу заставить функцию execv запускаться, если я напишу свой собственный исполняемый файл и запустю его, но если я попытаюсь запустить touch или echo, он вернет -1

#include <stdio.h>
#include <unistd.h> // exec functions
#include <sys/types.h> // pid_t
#include <sys/wait.h>

#define HIGH 1
#define LOW 0

int digitalWrite(int pin, short type) {

    pid_t pid = fork();
    if (pid == 0) {
        printf("pid == %i\n", pid);
        if (type == HIGH) {
            char* args[] = {"echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};
            int val = execv(args[0], args);
            printf("ran function execl, %i\n", val);
        } else {
            printf("Unable to do anything but set pin to HIGH\n");
        }
    } else if (pid < 0) { // pid < 0
        printf("fork failed\n");
    }

    wait(NULL);
}

int main() {

    printf("Starting digitalWrite\n");

    digitalWrite(0, HIGH);

    printf("Completed digitalWrite()\n");

    return 0;
}

Просто для контекста вот моя сборка:

$ gcc wiringbeagle.c 
$ ./a.out 
Starting digitalWrite
pid == 0
ran function execl, -1
Completed digitalWrite()
Completed digitalWrite()
$ ls
a.out  wiringbeagle.c

Команда echo 1 > /sys/class/gpio/gpio67/value отлично работает в терминале сама по себе, и если я создаю локальный файл (т. Е. Коснусь tmpfile.txt) и пытаюсь запустить echo hi > tmpfile.txt, она запускается, как и ожидалось, в моей командной строке, ноне запускается в программе.

Я, должно быть, не понимаю что-то с execv, и любая помощь будет принята с благодарностью!

Ответы [ 3 ]

3 голосов
/ 11 мая 2019

Первый аргумент execv - это файл для выполнения. В отличие от вашей оболочки, execv не выполняет поиск в каталогах, указанных в переменной среды PATH, поэтому вам необходимо указать полный путь к исполняемому файлу. Если в вашем текущем рабочем каталоге нет исполняемого файла с именем echo, execv("echo",...) завершится с ошибкой «файл не найден». (Используйте perror, чтобы получить лучшие сообщения об ошибках).

Если вы хотите выполнить поиск исполняемого файла, как это делает оболочка, используйте execvp. Но обратите внимание, что ваша оболочка, вероятно, выполняет echo как встроенную команду, поэтому она не будет такой же echo, как ваша оболочка. В этом случае это нормально.

Как только вы исправите это, вы столкнетесь с другой проблемой. Поскольку вы просто вызываете утилиту командной строки с аргументами, а не используете оболочку, аргумент ">" является просто аргументом. Это оболочка, которая обрабатывает перенаправления (а также каналы, цитаты и кучу других полезных вещей). Поэтому все, что вам нужно сделать, - это отправить три аргумента на стандартный вывод.

Вы можете использовать функцию system для выполнения команды, используя оболочку, или вы можете сами настроить перенаправление с помощью freopen ввода stdout вашего ребенка перед выполнением execvp.

Вы можете получить довольно много информации о системных интерфейсах, используя команду man. Например, чтобы узнать, что делает freopen, используйте man freopen. Вы также можете читать справочные страницы в Интернете, например. http://man7.org/linux/man-pages/man3/freopen.3.html,, но документация по вашей собственной системе прямо здесь, а также относится к фактической версии программного обеспечения, установленного в вашей системе (при условии, что вы установили документацию).

2 голосов
/ 11 мая 2019

Я не совсем уверен, почему вы даже используете семейство exec для запуска внешних программ в этом случае.Стандартная библиотека C предоставляет идеально адекватных файловых устройств ввода / вывода.

Например, вы можете просто fopen, fprintf и fclose файл без когда-либо запускать другой внешний процесс, который будет выполнять эту работу за вас:

int bytesWrit = 0;
FILE *gpioHndl = fopen("/sys/class/gpio/gpio67/value");
if (gpioHndl != NULL) {
    bytesWrit = fprintf(gpioHndl, "1\n");
    fclose(gpioHndl);
}
if (bytesWrit != 2) {
    HandleError();
}

Вероятно, это предпочтительный способ сделать то, что вы хотите, - просто записать фиксированное значение в файл.


С точки зрения того, почему ваш execv звонок не работает (хотя это совершенно неважно, если вы примите мой совет выше), есть несколько вещей, о которых вам нужно знать.

Во-первых,в то время как некоторые команды на самом деле являются файлами на диске, которые вы можете exec, другие могут быть внутренними bash командами (a) .В моей системе, например:

pax:~$ type ftp
ftp is /usr/bin/ftp

pax:~$ type echo
echo is a shell builtin

Один из способов решить эту проблему - запустить фактический исполняемый файл bash (который, будучи командой на диске, может быть выполнен черезexec), указав ему выполнить внутреннюю команду echo.Из командной строки:

pax:~$ bash -c 'echo xyzzy'
xyzzy

Во-вторых, если вы хотите использовать перенаправление, это обычно то, что делается с помощью оболочки , , а не вызовов exec или отдельных исполняемых файлов.

Попытка выполнить перенаправление через семейство exec обычно приводит только к тому, что >somefile передается в качестве параметра literal в исполняемый файл (в массиве argv), не используется для прикрепления стандартного вывода к файлу.Другими словами, он не будет работать, если исполняемый файл специально не обрабатывает перенаправление, что редко.

Это означает, что вам придется запускать оболочку с перенаправлением и запускать исполняемый файл после выполнения этих перенаправлений, даже если команда не является внутренней.

В-третьих, если вы хотите, чтобы путь, по которому выполняется поиск вашего исполняемого файла, execvp - это требуемый вызов, а не execv (последняя простоиспользует файл, который вы явно предоставили, либо относительно текущего рабочего каталога, либо по абсолютному пути, например /bin/ls).Итак, в вашем случае вы должны:

  • использовать execvp для поиска пути;или
  • полностью укажите путь с помощью execv.

(a) Команда echo, пока она bash -Внутренний также может быть предоставлен как отдельный исполняемый файл (я полагаю, что Posix требует этого), так что это может не быть проблемой здесь.Это может быть проблемой, если вы ожидаете, что они будут действовать точно так же с точки зрения более эзотерических аргументов: -)

1 голос
/ 11 мая 2019

execv() не ищет переменную окружения PATH, чтобы найти исполняемый файл.Для справочной страницы Linux execv() (добавлен жирный текст):

...

Специальная семантика для execlp(), execvp() и execvpe()

Функции execlp(), execvp() и execvpe() дублируют действия оболочки при поиске исполняемого файла , если указанное имя файла не соответствуетсодержит косую черту (/) ....

...

Итак, эти три будут искать переменную среды PATH, если переданное имя файла не содержит / символа.

Вы используете execv(), который не является одним из этих трех.Поэтому execv() будет не искать переменную среды PATH.

Поскольку в вашем текущем рабочем каталоге нет исполняемого файла с именем echo, execv() завершается с ошибкой.

Вам необходимо использовать execvp() для справочной страницы.

...