我遇到了一个问题,我无法在名称长度超过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;
}
此脚本将在_________.....___.txt
下创建0220aaaa...aaa/
文件,但不在0221aaaa...aaa/
下。
是否存在此问题的原因,如何更改脚本以便在所有目录中创建*.txt
文件?
脚本在运行时不会消失或产生任何错误消息。
这个问题确实 已经在Why does the 260 character path length limit exist in Windows?中得到答案正如另一个问题及其答案清楚地表明的那样,此路径长度限制是Windows问题,而不是 NTFS 问题。 NTFS允许长度最多为32K字符的路径。
实际上,我的测试脚本能够创建最多255个字符的目录(正如NTFS规范所预期的那样),当目录名长度超过220个字符时,似乎无法在目录中存储文件。
答案 0 :(得分:4)
问题的根源是您尝试验证文件是否已创建的方法。有关更多信息,请阅读以下详细说明。
以下是事实:
perl
或mkdirL
来电时,openL
不会出现任何错误。perl
可以使用openL
打开创建的文件并阅读内容。因此问题是由于您使用的任何工具使用ANSI版本的Windows API调用,或指定相对路径或两者的组合,因此它们的路径限制为260个字符。 / p>
为了测试这一点,我在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);
你会得到:
因此,请提供您不能依赖的任何外部程序的短路径来使用Unicode接口。
现在我有机会在64-bit Windows 8.1 system上尝试这个,我无法复制这个问题。
以下是我使用的代码:
#!/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";
}
这是输出:
C:\…\Temp> perl tt.pl 410 411 412 413 414 415 … 460 461 462 463 464 465
我还附上了几个屏幕截图:
现在,我可以报告资源管理器在C:\...\Temp\test\0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
并且GVim无法打开后续目录中的文件。也就是说,从脚本中发出的system 'c:/.../gvim.exe', $file_name;
会导致
据推测,GVim会从Naming Files, Paths, and Namespaces
进入以下版本由于您不能将
"\\?\"
前缀与相对路径一起使用,因此相对路径始终限制为总共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";
}
输出:
… 459 Hello! 460 Hello! 461 Hello! 462 Hello! 463 Hello! 464 Hello! 465 Hello!
因为Win32::LongPath
内部规范化路径,所以它们遵循“指定扩展长度路径,使用"\\?\"
前缀”建议,然后使用Unicode版本API调用,例如CreateFileW,openL
不会遇到此类问题。
在此函数的ANSI版本中,名称仅限于
MAX_PATH
个字符。要将此限制扩展为32,767个宽字符,请调用函数的Unicode版本并将"\\?\"
添加到路径中。有关详细信息,请参阅Naming Files, Paths, and Namespaces。
您如何验证文件是否已正确创建?
另请参阅“Why is Perl system call failing to invoke internal Windows command?”了解qx{cmd /c rd /s /q test}