如何在execvp调用find时使用"*.c"通配符?目前仅支持完整文件名
我来帮你分析这个问题——其实核心原因是通配符的解析是shell的工作,不是execvp或者find本身,咱们一步步理清楚:
为什么直接用"*.c"不行?
当你在终端敲find -name *.c时,是shell先把*.c展开成当前目录下所有.c结尾的文件名,再把这些文件名传给find——但这其实是错误的用法,因为find的-name选项只接受一个匹配模式,这样会导致find报错(比如提示paths must precede expression)。
正确的shell用法是用引号把*.c括起来:find -name '*.c',这时候shell不会扩展通配符,而是直接把"*.c"作为参数传给find,由find自己解析这个匹配模式,遍历目录找到所有符合条件的文件。
你的代码里直接传"one.c"能正常运行,是因为这是一个具体的文件名,不需要解析;换成"*.c"后,其实相当于shell里的find -name '*.c',理论上应该能找到所有.c文件,如果你觉得不行,大概率是没加错误提示,看不到execvp或find的报错信息。
解决方案1:让find自己处理通配符(推荐,符合find的正确用法)
修改你的代码,把"one.c"换成"*.c",同时加上execvp失败后的错误提示(因为execvp成功的话,后面的代码不会执行,一旦执行就说明出错了):
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(void) { printf("running\n"); char* args[] = { "find", "-name", "*.c", NULL}; char * envp[] ={NULL}; int pid = fork(); switch(pid){ case -1: perror("fork() failed"); exit(1); case 0: // child process execvp(args[0], args); // 能走到这里说明execvp执行失败 perror("execvp failed"); exit(1); default: // parent process if(wait(NULL) == -1){ perror("wait() failed"); } } return 0; }
这样find会自己解析*.c模式,遍历当前目录及子目录找到所有.c结尾的文件。
解决方案2:调用shell来处理通配符(仅当你确实需要shell扩展时用)
如果你非要让*.c先被展开成具体文件名(虽然这不符合find的常规用法),可以通过execvp调用sh -c来让shell完成通配符扩展:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(void) { printf("running\n"); // 让shell执行完整的find命令,由shell处理通配符 char* args[] = { "sh", "-c", "find -name *.c", NULL}; char * envp[] ={NULL}; int pid = fork(); switch(pid){ case -1: perror("fork() failed"); exit(1); case 0: execvp(args[0], args); perror("execvp failed"); exit(1); default: if(wait(NULL) == -1){ perror("wait() failed"); } } return 0; }
注意:这种方式会让shell先把*.c展开成当前目录的.c文件,再传给find,这可能导致find报错(因为-name只能接收一个参数),所以仅在你有特殊需求时使用。
额外补充:手动在程序中扩展通配符
如果你想自己在代码里处理通配符扩展,可以用glob函数实现,不过同样要注意find的语法限制:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #include <glob.h> int main(void) { printf("running\n"); glob_t glob_result; // 手动展开*.c通配符 int ret = glob("*.c", GLOB_ERR, NULL, &glob_result); if (ret != 0) { perror("glob failed"); exit(1); } // 构建参数数组:注意这种用法不符合find的语法,仅作演示 char** args = malloc((3 + glob_result.gl_pathc) * sizeof(char*)); args[0] = "find"; args[1] = "-name"; for (int i = 0; i < glob_result.gl_pathc; i++) { args[2 + i] = glob_result.gl_pathv[i]; } args[2 + glob_result.gl_pathc] = NULL; char * envp[] ={NULL}; int pid = fork(); switch(pid){ case -1: perror("fork() failed"); exit(1); case 0: execvp(args[0], args); perror("execvp failed"); exit(1); default: if(wait(NULL) == -1){ perror("wait() failed"); } } globfree(&glob_result); free(args); return 0; }
内容的提问来源于stack exchange,提问作者code kid

