【问题标题】:Moving files to different linux directory将文件移动到不同的linux目录
【发布时间】:2017-04-22 00:28:05
【问题描述】:

我正在编写一个小程序,通过创建新目录并根据文件类型在其中存储文件来组织 linux 中的目录。我知道我需要使用 rename() 函数,但是如何继续移动具有不同名称的文件?

假设我的目录中有一个.mkv 文件。我的程序检测文件类型并将文件移动到名为video 的目录中(在我当前工作的目录中)。如何传递文件的名称,以便在移动文件时保留其名称?

以下是我对它应该如何工作的最佳猜测,但显然无法编译。如何以可以编译的方式复制它?

我已经有一个循环运行readdir(),并且在每个循环中它检测每个条目的文件类型并使用该文件类型将文件移动到另一个目录中,所以rename() 被调用内部readdir() 循环内的 if 语句。

DIR *d; 
struct dirent *dp; 
d = opendir(".");
//if statement with function to find file type
//if the file is an .mkv, it runs: 
rename(dp->d_name, "./video/%s", dp->d_name);
//how can I do this in a way that will compile?  

示例:
如果我有一个名为scarface.mkv 的文件,它将创建一个目录video(已经完成)并将scarface.mkv 移动到video 目录中。

【问题讨论】:

  • 您不能将 格式说明符 传递给 rename(例如,%srename 中没有意义)。使用sprintfsnprintfscarface.mkv 构建newpath 并将连接的字符串传递给renamerename 的原型是 int rename(const char *oldpath, const char *newpath); 由您来正确创建 nul-terminated oldpathnewpath。 (man 2 rename 是你的朋友)

标签: c linux directory


【解决方案1】:

您不能提供完整的相关代码吗?您不是在此处初始化 dp,而是在 rename 调用中使用它。

rename 有 2 个参数,它们都是指向字符串的指针(即字符数组)。您的代码使用 3。

%s 看起来像一个格式字符串,但你没有用例如扩展它。 sprintf 和一个用于替换 %s 的附加参数。

【讨论】:

  • 我知道我的重命名不正确。我正在尝试向您展示我正在尝试做的事情,即使用我存储在“dp->d_name”变量中的名称创建一个目录。
  • 你可能应该 stat() 目录,如果它不存在 mkdir() 它
【解决方案2】:

继续我的评论,大多数新的 C 程序员感到困惑的地方在于没有考虑到手头的整个问题。您根本不能指望打电话给rename 并让它负责整个过程。 C 库函数通常做一件事,而且只做一件事。程序员需要考虑从头到尾的整个过程,并编写renamereaddir 函数未提供的所有其他难题。

例如,如果您计划传递一个新目录名称,您需要验证您打算移动文件的目录是否确实存在(或你需要创建它)。你如何测试新目录的存在?您还需要确定并验证您将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

检查一下,如果您有任何问题,请告诉我。它本身并不难,但是要提供一个相当稳健的移动,需要考虑很多事情(您可以添加更多验证,这只是一个最小示例)。

【讨论】:

    【解决方案3】:

    opendir() 只是第一个函数。它为您提供了一个可以使用的 DIR 结构,仅此而已。要获取文件列表,您需要一个循环来运行 readdir()。查看完整文档:

    https://www.gnu.org/software/libc/manual/html_node/Accessing-Directories.html#Accessing-Directories

    但是如果可以的话,使用 glob() 比 opendir() 容易得多。见:

    http://man7.org/linux/man-pages/man3/glob.3.html

    【讨论】:

    • 对不起,我应该明确表示我已经有一个循环来运行 readdir(),并且在每个循环中它检测每个条目的文件类型并使用该文件类型将文件移动到另一个目录,所以 rename() 在 readdir() 循环内的 if 语句中被调用。
    • 如果你已经有了循环,那么你正确地迭代了目录。编译失败一定是你使用了一些不正确的语法造成的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-05-07
    • 1970-01-01
    • 2021-05-25
    • 2019-03-03
    • 2018-04-07
    • 2014-01-23
    相关资源
    最近更新 更多