exec 功能仅 运行 宁一些命令,不会 运行 回显

exec function only running some commands, won't run echo

我正在尝试通过 exec 函数族 运行 命令行参数(特别是 echo)。如果我编写自己的可执行文件并 运行 它,我可以获得 运行 的 execv 函数,但是如果我尝试 运行 触摸或回显它 returns -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 不理解,如有任何帮助,我们将不胜感激!

execv() 不搜索 PATH 环境变量以查找可执行文件。根据 the Linux execv() man page(添加了粗体文本):

...

Special semantics for execlp(), execvp(), and execvpe()

The execlp(), execvp(), and execvpe() functions duplicate the actions of the shell in searching for an executable file if the specified filename does not contain a slash (/) character. ...

...

因此,如果传递的文件名不包含 / 字符,这三个将搜索 PATH 环境变量。

您正在使用 execv(),它不是这三个之一。因此,execv() 不会 搜索 PATH 环境变量。

由于您当前的工作目录不包含名为 echo 的可执行文件,execv() 失败。

每个 the man page.

需要使用 execvp()

我不完全确定为什么在这种情况下您甚至 使用 exec 系列到 运行 外部程序。 C 标准库提供 完美 足够的文件 I/O 东西。

例如,您可以简单地 fopenfprintffclose 文件 而无需 启动另一个外部进程来执行此操作为你工作:

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 可执行文件(作为 on-disk 命令, 可以 完成通过 exec),告诉它 运行 它的内部 echo 命令。从命令行:

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

其次,如果要使用重定向,这通常是由 shell、 而不是 exec 调用或单个可执行文件完成的。

尝试通过 exec 系列进行重定向通常只会导致 >somefile 作为 文字 参数传递给可执行文件(在argv array), not 用于将标准输出附加到文件。换句话说,除非可执行文件专门处理重定向,否则它不会工作,这种情况很少见。

所以这意味着你将必须 运行 带有重定向的 shell 并在执行这些重定向后将其 运行 为可执行文件,甚至 if 该命令不是内部命令。

第三,如果你想搜索你的可执行文件的路径,execvp 是你想要的调用,而不是 execv(后者只使用你明确提供的文件,或者相对于当前工作目录或像 /bin/ls 这样的绝对路径)。因此,在您的情况下,您应该:

  • 使用execvp搜索路径;或
  • 完全指定路径execv

(a) echo 命令虽然是 bash 内部的,但也可以作为单独的可执行文件提供(我相信 Posix 需要这个),所以这在这里可能不是问题。如果您希望他们在更深奥的论点方面表现得完全相同,可能会成为一个问题:-)

execv的第一个参数是要执行的文件。与您的 shell 不同,execv 不会搜索 PATH 环境变量指示的目录,因此您需要为其提供可执行文件的完整路径。除非在您当前的工作目录中有一个名为 echo 的可执行文件,否则 execv("echo",...) 将失败并出现 "file not found" 错误。 (使用 perror 以获得更好的错误消息)。

如果您想像 shell 那样搜索可执行文件,请使用 execvp。但请注意,您的 shell 可能将 echo 作为 built-in 命令执行,因此它不会与您的 shell 使用的 echo 相同。在这种情况下,没关系。

一旦你解决了这个问题,你就会遇到一个不同的问题。由于您只是调用带有参数的 command-line 实用程序,而不是使用 shell,因此参数 ">" 只是一个参数。 shell 处理重定向(以及管道、引用和其他一些有用的东西)。所以你要做的就是将三个参数发送到标准输出。

您可以使用 system 函数使用 shell 执行命令,或者您可以在执行 execvp

您可以使用man 命令获取有关系统界面的大量信息。例如,要了解 freopen 的作用,请使用 man freopen。您还可以在 Internet 上阅读联机帮助页,例如。 http://man7.org/linux/man-pages/man3/freopen.3.html,但你自己系统上的文档就在那里,也适用于你系统上安装的软件的实际版本(假设你安装了文档)。

您需要使用绝对路径作为execv中的第一个参数 那么,正确的是:

char* args[] = {"/bin/echo","echo", "1", ">", "/sys/class/gpio/gpio67/value", NULL};

但是运行你想要什么(在文件'/sys/class/gpio/gpio67/value'中输入值'1'),你需要使用命令 sh:

char* args[] = {"/bin/sh", "sh","-c", "/bin/echo 1 > /sys/class/gpio/gpio67/value", NULL};

“sh -c”的参数是一个字符串。然后,您需要将所有命令放在一起作为一个字符串