【问题标题】:exec function only running some commands, won't run echoexec 函数只运行一些命令,不会运行 echo
【发布时间】:2019-05-11 00:22:11
【问题描述】:

我正在尝试通过 exec 系列函数运行命令行参数(特别是 echo)。如果我编写自己的可执行文件并运行它,我可以运行 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 &gt; /sys/class/gpio/gpio67/value 在终端中运行良好,如果我创建一个本地文件(即 touch tmpfile.txt)并尝试运行 echo hi &gt; tmpfile.txt 它在我的命令行中按预期运行但没有在程序中运行。

我一定不理解 execv 的某些内容,非常感谢任何帮助!

【问题讨论】:

  • 我很好奇你为什么要使用 exec 为你做这件事。 C 提供标准库中内置的文件 I/O。

标签: c exec echo


【解决方案1】:

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

如果您想像 shell 一样搜索可执行文件,请使用 execvp。但请注意,您的 shell 可能将 echo 作为内置命令执行,因此它与您的 shell 使用的 echo 不同。在这种情况下,这很好。

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

您可以使用system 函数使用shell 执行命令,或者您可以在执行execvp 之前通过freopening 孩子的stdout 自己设置重定向。

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

【讨论】:

    【解决方案2】:

    我不完全确定您为什么在这种情况下使用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 可执行文件(作为磁盘上的命令,可以通过exec 完成),告诉它运行其内部@ 987654332@ 命令。从命令行:

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

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

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

    这意味着您必须使用重定向运行 shell 并在执行这些重定向后让它运行可执行文件,即使 if 命令不是内部命令。

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

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

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

    【讨论】:

      【解决方案3】:

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

      ...

      execlp()execvp()execvpe() 的特殊语义

      execlp()execvp()execvpe() 函数复制操作 搜索可执行文件时的 shell 如果指定 文件名不包含斜杠 (/) 字符。 ...

      ...

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

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

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

      您需要使用 execvp()the man page.

      【讨论】:

        【解决方案4】:

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

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

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

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

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

        【讨论】:

          猜你喜欢
          • 2021-11-27
          • 1970-01-01
          • 1970-01-01
          • 2021-01-03
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多