gpt4 book ai didi

perl - 使用 readdir 时重命名文件是否安全?

转载 作者:行者123 更新时间:2023-12-04 23:16:50 24 4
gpt4 key购买 nike

使用 readdir 扫描目录时,你能安全地重命名文件而不用担心进入无限递归吗?例如:

use v5.12;  # make readdir set $_ in while loops
use strict;
use warnings;

use File::Spec;

my $dir = 'tdir';
opendir ( my $dh, $dir ) or die "Could not open dir '$dir': $!";
while (readdir $dh) {
next if /^\.\.?\z/;
my $filename = File::Spec->catfile( $dir, $_ );
if ( -f $filename) {
my $newname = File::Spec->catfile( $dir, "prefix_$_" );
rename ($filename, $newname) or warn $!;
}
}

closedir $dh;

因此,例如重命名后 fileprefix_file , readdir不会找到 prefix_filewhile 的后续迭代中循环(然后再次将其重命名为 prefix_prefix_file 等等?很明显它不会这样做,但由于我在文档中找不到它,我无论如何都会问这个问题。

最佳答案

回答

底层系统调用是POSIX的 readdir() ,并且规范说:

If a file is removed from or added to the directory after the most recent call to opendir() or rewinddir(), whether a subsequent call to readdir() returns an entry for that file is unspecified.



它只是意味着您可能会或可能不会看到这些文件。您可能会发现特定平台确实指定了发生的情况,但它可能无法移植到其他系统。

示范

ikegami asked :

rename neither adds nor removes any directory entries, though. It just edits one.



我回答了:

It (rename()) changes an entry in the directory; what happens depends on how [the file system] is implemented. If you change the file name from a to humongous-long-name-that-is-too-boring-to-be-believable, there's a decent chance that the entry will move in the directory on disk, leading to unspecified behaviour [as noted in the main answer]. … Whether … rename() actually screws up a scan with readdir() depends on the system (operating system and file system), which is all that I claimed.



经过进一步讨论,我创建了这个示例,说明在一个特定系统上可以发生和确实发生的事情。我使用了以下步骤:
  • 创建一个目录——它的名字无关紧要。
  • 切换到那个目录。
  • 复制 readdir.cmake.files.sh进入目录。
  • 创建程序 readdir来源 readdir.c (例如,使用 make readdir )。
  • 该代码假定 struct dirent包括成员(member) d_namlen这不是 POSIX 规定的。
  • 没有它是可行的(但需要一些小的改动)。
  • 创建文件(或目录)a .
  • 运行 ./readdir .当它提示你时按回车。你应该看到
    输出与此类似,但 inode 编号会有所不同。

  •     $ ./readdir
    44249044: ( 1) .
    42588881: ( 2) ..
    44260959: ( 10) .gitignore
    44398380: ( 1) a
    Found entry 'a' - hit return to continue:
    Continuing...
    44398371: ( 10) make.files
    44398280: ( 13) make.files.sh
    44398338: ( 8) makefile
    44398351: ( 7) readdir
    44260963: ( 9) readdir.c
    44398352: ( 12) readdir.dSYM
    44260960: ( 9) README.md
    44398364: ( 6) rename
    44260964: ( 8) rename.c
    44398365: ( 11) rename.dSYM
    $
  • 运行 sh make.files.sh .这将创建文件 moderately-long-file-name.000 .. moderately-long-file-name.999 .
  • 运行 ./readdir再次。不要点击返回。
  • 切换到不同的终端窗口。
  • 将目录更改为正在运行测试的目录。
  • 运行:mv a zzz-let-sleeping-file-renames-lie-unperturbed
  • 切换回运行 readdir 的终端窗口.
  • 按回车。您可能会看到类似于以下内容的输出:

  •     $ ./readdir
    44249044: ( 1) .
    42588881: ( 2) ..
    44260959: ( 10) .gitignore
    44398380: ( 1) a
    Found entry 'a' - hit return to continue:
    Continuing...
    44398371: ( 10) make.files
    44398280: ( 13) make.files.sh
    44398338: ( 8) makefile
    44431473: ( 29) moderately-long-file-name.000
    44431474: ( 29) moderately-long-file-name.001
    44431475: ( 29) moderately-long-file-name.002
    ...
    44432470: ( 29) moderately-long-file-name.997
    44432471: ( 29) moderately-long-file-name.998
    44432472: ( 29) moderately-long-file-name.999
    44398351: ( 7) readdir
    44260963: ( 9) readdir.c
    44398352: ( 12) readdir.dSYM
    44260960: ( 9) README.md
    44398364: ( 6) rename
    44260964: ( 8) rename.c
    44398365: ( 11) rename.dSYM
    44398380: ( 45) zzz-let-sleeping-file-renames-lie-unperturbed
    $

    这是我在 Mac OS X 10.11.6 El Capitan 上使用默认 HFS+ 获得的结果
    文件系统。当目录很小(没有中等长度的
    文件名),则重命名的文件没有出现。当额外
    创建的文件使目录大小约为 34 KiB,然后
    重命名的文件确实出现了。

    这表明在某些文件系统上(特别是 Apple 的 HFS+)
    在某些情况下, readdir()扫描目录是
    受文件重命名操作的影响。如果您想编写和使用 rename命令而不是使用 mv ,就这样吧——当我尝试时,
    这对结果没有影响。

    结论

    在其他文件系统或其他操作系统上,YMMV。然而,这
    足以证明在某些系统上,重命名文件而 readdir()扫描正在进行中可能会出现相同的"file"
    在输出中两次。
    make.files.sh
    #!/bin/sh

    for file in $(seq -f 'moderately-long-file-name.%03.0f' 0 999)
    do > "$file"
    done
    readdir.c
    /* SO 3901-5527 - attempt to demonstrate renaming moving entries */
    #include <dirent.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>

    static const char *stop_after = "a";

    static void process_directory(const char *dirname)
    {
    DIR *dp = opendir(dirname);

    if (dp == 0)
    fprintf(stderr, "Failed to open directory %s\n", dirname);
    else
    {
    struct dirent *entry;
    while ((entry = readdir(dp)) != 0)
    {
    /* Ignore current and parent directory */
    printf("%8d: (%3d) %s\n", (int)entry->d_ino, entry->d_namlen, entry->d_name);
    if (strcmp(entry->d_name, stop_after) == 0)
    {
    printf("Found entry '%s' - hit return to continue: ", stop_after);
    fflush(stdout);
    char *buffer = 0;
    size_t buflen = 0;
    getline(&buffer, &buflen, stdin);
    free(buffer);
    printf("Continuing...\n");
    }
    }
    closedir(dp);
    }
    }

    int main(int argc, char **argv)
    {
    int opt;
    while ((opt = getopt(argc, argv, "s:")) != -1)
    {
    switch (opt)
    {
    case 's':
    stop_after = optarg;
    break;;
    default:
    fprintf(stderr, "%s: Unrecognized option '-%c'\n", argv[0], optopt);
    fprintf(stderr, "Usage: %s [-s stop_after] [directory ...]\n", argv[0]);
    return(EXIT_FAILURE);
    }
    }
    if (optind == argc)
    process_directory(".");
    else
    {
    for (int i = optind; i < argc; i++)
    process_directory(argv[i]);
    }
    return(0);
    }

    关于perl - 使用 readdir 时重命名文件是否安全?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39015527/

    24 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com