exec函数仅运行某些命令,不会运行echo

时间:2019-05-11 00:22:12

标签: c exec echo

我试图通过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 > /sys/class/gpio/gpio67/value在终端上运行正常,如果我创建本地文件(例如,触摸tmpfile.txt)并尝试运行echo hi > tmpfile.txt,则该命令将在命令行中按预期运行但无法在程序中运行。

我一定对execv不太了解,不胜感激!

4 个答案:

答案 0 :(得分:3)

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

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

修复该问题后,将遇到另一个问题。由于您只是使用参数而不是使用Shell来调用命令行实用程序,因此参数">"只是一个参数。它是处理重定向的外壳(以及管道,引用和许多其他有用的东西)。因此,您要做的就是将三个参数发送到stdout。

您可以使用system函数通过外壳执行命令,也可以通过freopen在您的孩子中输入stdout来自己设置重定向,然后再进行execvp

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

答案 1 :(得分:2)

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

例如,您可以简单地{<1>},fopenfprintf文件,而无需启动其他外部过程来为您执行此操作:< / p>

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命令 (a)。在我的系统上,例如:

bash

解决此问题的一种方法是运行实际的pax:~$ type ftp ftp is /usr/bin/ftp pax:~$ type echo echo is a shell builtin 可执行文件(作为磁盘命令, 可以通过bash完成),告诉它运行其内部exec命令。从命令行:

echo

第二,如果要使用重定向,通常这是由 shell 完成的,而不是pax:~$ bash -c 'echo xyzzy' xyzzy 调用或单个可执行文件。

尝试通过exec系列进行重定向通常只会导致exec作为 literal 参数传递给可执行文件(在>somefile中数组),用于将标准输出附加到文件。换句话说,除非可执行文件专门处理重定向,否则这种方法是行不通的。

因此,这意味着您将必须通过重定向运行外壳程序,并在执行这些重定向后让外壳程序运行可执行文件,即使如果该命令不是内部命令。

第三,如果您要搜索可执行文件的路径,argv是您想要的调用,而不是execvp(后者只是使用您显式提供的文件,或者是相对于当前工作目录的相对文件)或像execv这样的绝对路径。因此,就您而言,您应该:

  • 使用/bin/ls搜索路径;或
  • 使用execvp完全指定路径。

(a) execv命令(内部为echo)也可以作为单独的可执行文件提供(我相信Posix要求这样做),因此可能在这里不是问题。如果您希望他们在更深奥的论点上表现完全相同,可能会是一个问题:-)

答案 2 :(得分:1)

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

  

...

     

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

     

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

     

...

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

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

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

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

答案 3 :(得分:0)

您需要使用绝对路径作为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”的参数是一个字符串。然后,您需要将所有命令作为字符串放在一起