在Windows上使用Perl在名称长度超过220个字符的目录中创建文件

Ren*_*ger 5 windows perl ntfs

我遇到了一个问题,我无法在名称长度超过220个字符的目录下创建文件.

以下是一个测试脚本,它至少在我的机器上重现了观察到的行为:

use warnings;
use strict;

use Win32::LongPath;

print system ('rmdir /s /q test');
mkdirL('test');

for my $i (200 .. 255) {

  my $dir_name = 'test/' . sprintf("%04d", $i) . ('a' x ($i-4));
  mkdirL($dir_name);

  openL(\my $fh, '>', "$dir_name/" . ('_' x 200) . '.txt') or die "$^E";
  print $fh 'Hello!';
# closeL $fh;
}
Run Code Online (Sandbox Code Playgroud)

此脚本将_________.....___.txt在" 0220aaaa...aaa/但不在"下创建文件0221aaaa...aaa/.

是否存在此问题的原因,如何更改脚本以便*.txt在所有目录中创建文件?

更新:

该脚本在运行时不会消失或产生任何错误消息.

更新2:

这个问题还没有答案为什么Windows中存在260个字符的路径长度限制?正如另一个问题及其答案清楚地表明的那样,此路径长度限制是Windows问题,而不是NTFS问题.NTFS允许长度最多为32K字符的路径.

事实上,我的测试脚本能够创建最多255个字符的目录(正如NTFS规范所预期的那样),当目录名长度超过220个字符时,似乎无法在目录中存储文件.

Sin*_*nür 4

长话短说

问题的根源在于您尝试验证文件是否已创建的方法。欲了解更多信息,请阅读下面的详细说明。

以下是事实:

  1. perlmkdirL使用或调用时不会出现任何错误openL
  2. perl可以使用 . 打开创建的文件并读取内容openL

因此,问题是由于无论您使用什么工具,要么使用 ANSI 版本的 Windows API 调用,要么指定相对路径,要么两者的组合,因此它们的路径被限制为 260 个字符。

为了测试这一点,我在 下运行了脚本D:\t。你瞧,GVim 在以下情况下无法打开文件$i = 250

在 D:\t 下运行时,GVim 无法打开 $i=250 处的文件

D:\t是四个字符,\test另外是五个字符。因此,250 + 9 = 259,当您添加另一个 时,就会达到260\

使用shortpathL

尝试这个:

#!/usr/bin/env perl

use strict;
use warnings;

use Win32::LongPath;

`cmd /c rd /s /q test`;

mkdirL('test') or die "$^E";

my $dir_length = 255;
my $dir_name = 'test/'
             . sprintf("%04d", $dir_length)
             . ('a' x ($dir_length - 4))
;

mkdirL($dir_name) or die "$^E";

my $file_name = "$dir_name/" . ('z' x 200) . '.txt';

printf "% 3d\n", length $file_name;

openL(\my $fh, '>', $file_name) or die "$^E";

print $fh "Hello!\n" or die "$^E";

close $fh or die "$^E";

system 'notepad.exe', shortpathL($file_name);
Run Code Online (Sandbox Code Playgroud)

你会得到:

给定短路径时记事本可以打开

因此,请为您不能依赖使用 Unicode 接口的任何外部程序提供短路径。

长篇大论的解释

现在我有机会在64 位 Windows 8.1 系统上实际尝试此操作,但我无法复制该问题。

这是我使用的代码:

#!/usr/bin/env perl

use strict;
use warnings;

use Win32::LongPath;

`cmd /c rd /s /q test`;

mkdirL('test')
    or die "$^E";

for my $i (200 .. 255) {
    my $dir_name = 'test/' . sprintf("%04d", $i) . ('a' x ($i-4));
    mkdirL($dir_name) or die "$^E";

    my $file_name = "$dir_name/" . ('_' x 200) . '.txt';

    printf "% 3d\n", length $file_name;

    openL(\my $fh, '>', $file_name)
        or die "$^E";

    print $fh 'Hello!' or die "$^E";

    close $fh or die "$^E";
}
Run Code Online (Sandbox Code Playgroud)

这是输出:

C:\...\Temp> perl tt.pl          
 410                                                   
 第411章                                                   
 第412章                                                   
 第413章                                                   
 第414章                                                   
 第415章                                                   
……
 第460章    
 第461章    
 第462章    
 第463章    
 第464章    
 第465章

我还附上了一些屏幕截图:

显示创建的目录的资源管理器窗口

显示其中一个文件的 GVim 窗口

现在,我可以报告资源管理器在导航到任何目录时遇到问题C:\...\Temp\test\0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

并且GVim无法打开后续目录中的文件。也就是说,system 'c:/.../gvim.exe', $file_name;从脚本内部发出的结果是

GVim 在处理 221 个字符的目录名时遇到问题

大概 GVim 在命名文件、路径和命名空间中遇到了以下问题

由于不能将"\\?\"前缀与相对路径一起使用,因此相对路径始终限制为 MAX_PATH 字符总数。

巧的是,我的目录路径长度%TEMP%恰好是33个字符。加上 5( 的长度\test),我们得到 38。加上 221,我们得到259。现在,当您向该字符串添加目录分隔符时,您会遇到260

这让我想到,您在其下创建的工作目录的完整路径的长度是多少test

更好的消息是,perl能够读回所写的所有内容:

#!/usr/bin/env perl

use strict;
use warnings;

use Win32::LongPath;

`cmd /c rd /s /q test`;

mkdirL('test')
    or die "$^E";

for my $i (220 .. 255) {
    my $dir_name = 'test/' . sprintf("%04d", $i) . ('a' x ($i-4));
    mkdirL($dir_name) or die "$^E";

    my $file_name = "$dir_name/" . ('_' x 200) . '.txt';

    printf "% 3d\n", length $file_name;

    openL(\my $fh, '>', $file_name)
        or die "$^E";

    print $fh 'Hello!' or die "$^E";

    close $fh or die "$^E";

    openL(\my $in, '<', $file_name)
        or die "$^E";

    print <$in>, "\n" or die "$^E";

    close $in or die "$^E";
}
Run Code Online (Sandbox Code Playgroud)

输出:

……
 第459章  
你好!
 第460章  
你好!
 第461章  
你好!
 第462章  
你好!
 第463章  
你好!
 第464章  
你好!
 第465章  
你好!

因为Win32::LongPath内部标准化了路径,所以它们遵循“要指定扩展长度路径,使用"\\?\"前缀”建议,然后使用 Unicode 版本的 API 调用,例如CreateFileWopenL不会遇到此类问题。

在此函数的 ANSI 版本中,名称仅限于MAX_PATH字符。要将此限制扩展到 32,767 个宽字符,请调用该函数的 Unicode 版本并添加"\\?\"到路径前面。有关详细信息,请参阅命名文件、路径和命名空间

您如何验证文件是否已正确创建?

另外,请参阅“为什么 Perl 系统调用无法调用内部 Windows 命令? ”以获取解释qx{cmd /c rd /s /q test}