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