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()
失败。
需要使用 execvp()
我不完全确定为什么在这种情况下您甚至 使用 exec
系列到 运行 外部程序。 C 标准库提供 完美 足够的文件 I/O 东西。
例如,您可以简单地 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
可执行文件(作为 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”的参数是一个字符串。然后,您需要将所有命令放在一起作为一个字符串
我正在尝试通过 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()
, andexecvpe()
The
execlp()
,execvp()
, andexecvpe()
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()
失败。
execvp()
我不完全确定为什么在这种情况下您甚至 使用 exec
系列到 运行 外部程序。 C 标准库提供 完美 足够的文件 I/O 东西。
例如,您可以简单地 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
可执行文件(作为 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”的参数是一个字符串。然后,您需要将所有命令放在一起作为一个字符串