继续我的评论,大多数新的 C 程序员感到困惑的地方在于没有考虑到手头的整个问题。您根本不能指望打电话给rename 并让它负责整个过程。 C 库函数通常做一件事,而且只做一件事。程序员需要考虑从头到尾的整个过程,并编写rename 或readdir 函数未提供的所有其他难题。
例如,如果您计划传递一个新目录名称,您需要验证您打算移动文件的目录是否确实存在(或你需要创建它)。你如何测试新目录的存在?您还需要确定并验证您将rename 文件添加到的新目录的格式。它是否包含尾随'/'?如果出现了怎么处理?
如果您只想移动具有特定扩展名的文件,则必须开发一种方法来检查并从readdir 返回的每个文件名中获取扩展名。获得当前文件的扩展名后,如何将其与目标扩展名进行比较?
最后,我如何将我打算将匹配文件移动到的目录名称与当前文件名结合起来以创建将文件移动到的完整路径?所有这些都是您需要提供的一些代码,以便能够将文件从一个目录移动到另一个目录。很难做到吗?不。这很容易,但您必须在该级别进行仔细考虑,并提供最低限度的验证,以确定您打算移动的任何单个文件的成功/失败。
而不是去思考如何创建拼图的每个部分,因为我知道您需要帮助,并且有一个合理的“经过”来帮助巩固您脑海中的拼图的各个部分,以下是一个简短的示例,可以完成我理解您的问题所要求的内容。下面将要搜索的目录作为 first 参数,将要搜索的 extension 作为 second 参数,以及 relative 或 absolute 路径名将任何匹配的文件移动到作为 第三个 参数(默认为 .(搜索当前目录)对于具有 "mkv" 扩展名的文件名,并移动到video 子目录):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h> /* PATH_MAX */
#include <sys/types.h> /* opendir */
#include <dirent.h> /* opendir, readdir */
#include <errno.h> /* errno */
#include <fcntl.h> /* for file constants */
#include <unistd.h> /* open/close */
int dir_exists (char *d);
char *fn_ext (char *fn);
char *fn_wext (char *s);
char *stripfwd (char *fn);
int main (int argc, char **argv) {
DIR *dp = opendir (argc > 1 ? argv[1] : ".");/* open directory (. default)*/
struct dirent *de = NULL; /* ptr to dirent for readdir */
char *srchext = argc > 2 ? argv[2] : "mkv", /* extension to search for */
*newdir = argc > 3 ? argv[3] : "./video",/* ptr newdir (video default)*/
path[PATH_MAX] = ""; /* array for trimmed newdir */
strcpy (path, newdir); /* copy newdir from read-only memory to array */
stripfwd (path); /* check for trailing '/' & overwrite with '\0' */
if (!dir_exists (path)) { /* validate new directory exists */
fprintf (stderr, "error: directory not found '%s'.\n", path);
return 0; /* or create/validate directory here */
}
while ((de = readdir (dp))) /* for each file in directory */
{
char *ext = NULL;
/* skip dot files */
if (!strcmp (de->d_name, ".") || !strcmp (de->d_name, ".."))
continue;
if ((ext = fn_ext (de->d_name)) == NULL) /* get file extension */
continue;
if (strcmp (srchext, ext) == 0) { /* if extensions match */
char newpath[PATH_MAX] = "", /* char array for newpath */
*fn = fn_wext (de->d_name); /* ptr to filename only */
sprintf (newpath, "%s/%s", path, fn); /* create newpath */
errno = 0;
if (rename (de->d_name, newpath) == -1) { /* rename/validate file */
fprintf (stderr, "error: move of '%s' to '%s' failed.\n",
de->d_name, newpath);
/* check errno here */
}
else /* output successful result */
printf ("moved '%s' to '%s'.\n", de->d_name, newpath);
}
}
return 0;
}
/** atomic test that directory exists (>=1 success, 0 otherwise)
* NOTE: no directory is actually created. fail occurs instead.
*/
int dir_exists (char *d)
{
int flags = O_DIRECTORY | O_RDONLY;
int mode = S_IRUSR | S_IWUSR;
int fd = open (d, flags, mode);
if (fd < 0) /* directory does not exist */
return 0;
else if (fd) { /* directory exists, rtn fd */
close (fd);
}
return fd;
}
/** Separates extension component from full filename string.
* Returns pointer following last '.' as extension, NULL otherwise.
* Protects against false return of ext followin '.' path.
* No memory is allocated, create copy of return to preserve.
*/
char *fn_ext (char *fn)
{
char *sp = NULL; /* start pointer */
char *ext;
if (!fn) return NULL;
if ((sp = strrchr (fn, '/'))) /* test for '/' to eliminate '.' in path */
sp++;
else
sp = fn;
if ((ext = strrchr (sp, '.')))
{
if (ext == fn) /* dot file case */
return NULL;
ext++;
}
else
ext = NULL;
return ext;
}
/** Separates filename component (with extension) from full filename string.
* Returns pointer following last '/' filename, full-string otherwise.
* No memory is allocated, create copy of return to preserve.
*/
char *fn_wext (char *s)
{
char *fn;
if ((fn = strrchr (s, '/')))
fn++;
else
fn = s;
return fn;
}
/** remove forward slash '/' at end of 'fn' */
char *stripfwd (char *fn)
{
size_t len = strlen (fn);
while (len && fn[len - 1] == '/')
fn[--len] = 0;
return fn;
}
当前目录中的示例文件
$ l myvideo_*
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_1.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_2.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_3.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_4.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_5.mkv
空的“视频”目录
$ l video
total 28
drwxr-xr-x 2 david david 4096 Apr 21 21:45 .
drwxr-xr-x 15 david david 24576 Apr 21 21:46 ..
编译/构建
$ gcc -Wall -Wextra -pedantic -std=gnu11 -Ofast -o bin/readdir_rename readdir_rename.c
使用/输出示例
$ ./bin/readdir_rename . mkv video
moved 'myvideo_2.mkv' to 'video/myvideo_2.mkv'.
moved 'myvideo_1.mkv' to 'video/myvideo_1.mkv'.
moved 'myvideo_3.mkv' to 'video/myvideo_3.mkv'.
moved 'myvideo_4.mkv' to 'video/myvideo_4.mkv'.
moved 'myvideo_5.mkv' to 'video/myvideo_5.mkv'.
确认移动
$ l video
total 28
drwxr-xr-x 2 david david 4096 Apr 21 21:46 .
drwxr-xr-x 15 david david 24576 Apr 21 21:46 ..
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_1.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_2.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_3.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_4.mkv
-rw-r--r-- 1 david david 0 Apr 21 21:46 myvideo_5.mkv
检查一下,如果您有任何问题,请告诉我。它本身并不难,但是要提供一个相当稳健的移动,需要考虑很多事情(您可以添加更多验证,这只是一个最小示例)。