system() 函数:最简调用方式
原理:直接执行字符串形式的系统命令,阻塞当前进程直到命令结束。
示例:
int main() {
// 执行系统命令(Windows/Linux通用格式)
int status = system("echo Hello, World!");
// 检查返回值:0表示成功,非0表示失败
if (status == 0) {
printf("命令执行成功\n");
} else {
printf("命令执行失败,错误码:%d\n", status);
}
return 0;
}
特点:
- ✅ 优点:简单易用,跨平台(Windows/Linux均支持)
- ❌ 缺点:无法获取命令输出,存在安全风险(如命令注入)
popen() 函数:捕获命令输出
原理:创建管道连接,读取命令的标准输出或输入。
示例(读取命令输出):
#include <stdio.h>
int main() {
FILE *fp;
char buffer[1024];
// 执行命令并读取输出("r"表示读取)
fp = popen("ls -l", "r"); // Linux示例 / Windows可改为"dir"
if (fp == NULL) {
perror("popen失败");
return 1;
}
// 逐行读取输出
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
printf("输出: %s", buffer);
}
// 关闭管道并获取命令退出状态
int status = pclose(fp);
if (status == -1) {
perror("关闭管道失败");
}
return 0;
}
特点:
- ✅ 可获取实时输出,支持双向数据流(用
"w"模式写入输入) - ❌ 不直接返回错误码,需通过
pclose()间接获取
exec() 函数族:精细控制进程
原理:替换当前进程为新的命令行进程,常与 fork() 配合使用。
示例(Linux下执行 ls):
#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
int main() {
pid_t pid = fork(); // 创建子进程
if (pid == 0) { // 子进程
execlp("/bin/ls", "ls", "-l", NULL); // 执行命令
perror("execlp失败"); // 仅当出错时执行
_exit(1);
} else if (pid > 0) { // 父进程
int status;
waitpid(pid, &status, 0); // 等待子进程结束
printf("子进程退出状态: %d\n", status);
} else {
perror("fork失败");
}
return 0;
}
关键函数:
execlp("命令路径", "参数0", "参数1", ..., NULL)execvp("命令名", 参数数组)
特点:- ✅ 无额外进程开销(直接替换当前进程)
- ❌ 需手动处理进程控制,Windows需用
CreateProcess()
安全与可移植性注意事项
- 命令注入防御:
- 避免使用用户输入直接拼接命令(如
system("rm " + user_input)) - 改用参数化形式(如
execvp单独传递参数)
- 避免使用用户输入直接拼接命令(如
- 跨平台适配:
- Windows路径使用反斜杠(
system("dir C:\\")) - Linux需检查权限(如
popen()可能因权限失败)
- Windows路径使用反斜杠(
- 错误处理:
- 检查所有系统调用的返回值(如
fork(),popen()) - 使用
perror()打印错误原因
- 检查所有系统调用的返回值(如
方法选择建议
| 场景 | 推荐方法 |
|---|---|
| 简单命令,无需输出 | system() |
| 需要捕获输出 | popen() |
| 精细控制进程参数 | fork() + exec() |
| Windows专属 | CreateProcess() |
引用说明:本文代码示例参考自《Unix环境高级编程》(Advanced Programming in the UNIX Environment, W. Richard Stevens)及GNU C Library文档,安全建议遵循CERT C安全编码标准。
原创文章,发布者:酷番叔,转转请注明出处:https://cloud.kd.cn/ask/6412.html