如何在C语言中执行shell命令
题主可以使用 exec 系列函数。这系列函数定义在 unistd.h 头文件中,所以使用前请包含这个头文件。这系列函数共有五个,
execl, execlp, execv, execvp, execle
其中常用的是前四个。前四个函数的原型为:
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
这四个函数的主要差别就在于参数的类型和用不用输入命令的绝对路径上。
以路径形式来分,凡是函数名中带 p 的(execlp,execvp)都只需要提供命令的名,函数会自动在当前的环境变量 $PATH 中查找命令的路径。而不带 p 的(execl,execv)必须要提供命令的绝对路径,否则函数会找不到这个命令的位置。这里以 execl 和 execlp 为例,以下运行
ls -l
命令的代码:
#include <stdio.h>
#include <unistd.h>
int main() {
// exec 系列函数出错时会返回 -1,平常返回 0,所以可以
// 据此来打印错误信息
// 第一个 ls 是命令的名称,execlp 函数会自动在 $PATH
// 中寻找这个命令。
// 后面一个 ls 是要在 shell 中输入的第一个参数
//(也就是命令名称本身)
// 使用 NULL 作为参数结尾标记是 exec 系列函数的要求。
if (execlp("ls", "ls", "-l", NULL) == -1)
perror("Error Executing Command.\n");
return 0;
}
在 shell 中运行这个 C 程序会输出
和你直接在 shell 中写 ls -l 的效果是一样的。然而,如果你使用不带 p 的 execl, 那么这样写就会报错。
#include <stdio.h>
#include <unistd.h>
int main() {
// execl 只接受命令的绝对路径,所以必须输入完整的
// 路径 /bin/ls,即
// if (execl("/bin/ls", "ls", "-l", NULL) == -1)
if (execl("ls", "ls", "-l", NULL) == -1)
perror("Error Executing Command.\n");
return 0;
}
输出结果为:
以参数类型来分,凡是函数名中带 l 的(execl,execlp)都需要把系统命令的参数全部传递给函数,而凡是函数名中带 v 的(execv,execvp)都需要把系统命令的参数统一放在一个数组里,然后把这个数组传递给函数。
比如刚才这个
#include <stdio.h>
#include <unistd.h>
int main() {
if (execlp("ls", "ls", "-l", NULL) == -1)
perror("Error Executing Command.\n");
return 0;
}
如果改用 execvp 来写的话就是
#include <stdio.h>
#include <unistd.h>
int main() {
// 这个字符串数组存有所有参数(包括命令名本身和
// 最后的 NULL)
char * argv[] = {"ls", "-l", NULL};
// 这里只需将命令名称和参数数组传递给 execvp 函数即可,
// 无需将参数一个个传递。同样函数会自动在 $PATH
// 中查找命令
if (execvp("ls", argv) == -1)
perror("Error Executing Command.\n");
return 0;
}
运行结果同样和直接写 ls -l 的效果相同。
execv 和 execvp 的区别也在于是否必须输入绝对路径,就不赘述了。
要注意的一点是,如果执行成功,exec 系列函数开启的新进程会完全代替当前的进程,也就是说当前进程会消失。所以一般会将 exec 和 fork 连用,先 fork 出一个子进程,然后在这个子进程中使用 exec 运行别的程序,防止父进程被 exec 覆盖。比如刚才的代码稍微改一下
#include <stdio.h>
#include <unistd.h>
int main() {
char * argv[] = {"ls", "-l", NULL};
if (execvp("ls", argv) == -1)
perror("Error Executing Command.\n");
// 加入一个 printf 语句
printf("Main process is still running.\n");
return 0;
}
运行后并不会出现 Main process is still running 这句话,因为 exec 后 main 函数执行产生的进程已经被 ls 命令产生的进程完全覆盖了,所以 exec 函数以下的语句是完全不会执行的。这时就可以使用 fork 来新建一个子进程,在子进程中执行 exec 函数。
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main() {
int r;
// fork() 大于零,父进程
if ((r = fork()) > 0) {
int status;
if (wait(&status) != -1) {
// 等待子进程退出
if(WIFEXITED(status)) {
printf("Main process is still running.\n");
return 0;
}
}
// fork () 等于零,子进程。子进程中运行 exec
} else if (r == 0) {
char * argv[] = {"ls", "-l", NULL};
if (execvp("ls", argv) == -1)
perror("Error Executing Command.\n");
return 0;
// fork() 小于零,出错
} else {
perror("Fork");
}
return 0;
}
这样运行结果就变成了
Main process is still running 这句话就会被输出到屏幕上。
1 函数原型:
int system(const char *cmd);
2 功能:
调用cmd内容的系统命令,即shell命令。
3 头文件:
stdlib.h
4 举例:
system("ls");
打印当前工作目录下的文件。
2、例如设置网卡IP为192.168.1.100,可以写作
system("ifconfig eth0 192.168.1.100");