在main函数中通过命令行传参调用execvp时出错的技术咨询
排查execvp执行命令时的错误问题
嘿,我看了你的代码和遇到的问题,这几个地方踩坑了,咱们一个个理清楚:
首先先把你的代码贴出来方便分析:
int main(int argc, char *argv[]){ pid_t pid; pid = fork(); //negative value is failed, 0 is newly created child process if(pid < 0){ fprintf(stderr, "Fork Failed"); exit(-1); }else if(pid == 0){ //fork() -> child process printf("You entered %d commands: \n", argc); argv[argc + 1] = NULL; execvp(argv[0],argv); }else{ wait(NULL); printf("child complete\n"); exit(0); } return 0; }
第一个坑:数组越界+多此一举的操作
你写的argv[argc + 1] = NULL;完全没必要,还会触发越界问题。C标准里明确规定,main函数的argv数组本身就会以NULL结尾,argv[argc]就是那个终止的NULL。你去修改argv[argc+1],这个位置已经超出了argv数组的合法范围,会导致未定义行为(比如程序崩溃或者奇怪的错误),直接删掉这行就行。
第二个核心坑:传参逻辑搞反了
你想执行ls -l /user/myuser,但代码里调用execvp(argv[0], argv)是错的——argv[0]是你自己编译出来的程序名(比如./myprog),不是ls!execvp会傻乎乎地去执行你的程序本身,而不是你要的ls命令。
另外,如果你是把整个ls -l /user/myuser当作一个参数传给程序(比如./myprog "ls -l /user/myuser"),那argv[1]会是这个完整字符串,execvp也没法识别出这是命令加参数,它会把整个字符串当作一个命令名去查找,自然找不到。
正确的做法是:
- 启动程序时把命令和每个参数分开传,比如:
./myprog ls -l /user/myuser
- 在代码里用
argv[1]作为要执行的命令,把&argv[1]作为参数数组传给execvp——这样execvp拿到的就是["ls", "-l", "/user/myuser", NULL],完全符合它的要求。
第三个坑:没加错误处理
execvp只有在执行失败的时候才会返回,所以调用完execvp之后一定要加错误处理,不然你根本不知道到底是找不到命令、参数错了还是别的问题。比如加上perror,就能直接打印出具体错误原因。
修正后的完整代码
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main(int argc, char *argv[]){ pid_t pid; pid = fork(); if(pid < 0){ fprintf(stderr, "Fork Failed\n"); exit(EXIT_FAILURE); }else if(pid == 0){ printf("You entered %d arguments: \n", argc); // 先检查有没有传入要执行的命令 if(argc < 2){ fprintf(stderr, "Please provide a command to execute\n"); exit(EXIT_FAILURE); } // 执行目标命令,参数数组从argv[1]开始 execvp(argv[1], &argv[1]); // 能走到这里就说明execvp失败了 perror("execvp failed"); exit(EXIT_FAILURE); }else{ wait(NULL); printf("child complete\n"); exit(EXIT_SUCCESS); } return 0; }
编译运行的命令:
gcc -o myprog myprog.c ./myprog ls -l /user/myuser
这样就能正确执行你想要的命令了。
内容的提问来源于stack exchange,提问作者Andy Ho
相关产品推荐
相关产品推荐

