在 Windows 上使用 Perl 在名称长度超过 220 个字符的目录中创建文件 [英] Creating a file using Perl on Windows in a directory the length of whose name exceeds 220 characters

查看:53
本文介绍了在 Windows 上使用 Perl 在名称长度超过 220 个字符的目录中创建文件的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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

使用警告;使用严格;使用 Win32::LongPath;打印系统('rmdir/s/q test');mkdirL('测试');对于我的 $i (200 .. 255) {我的 $dir_name = 'test/' .sprintf("%04d", $i) .('a' x ($i-4));mkdirL($dir_name);openL(\my $fh, '>', "$dir_name/" . ('_' x 200) . '.txt') 否则死 "$^E";打印 $fh '你好!';# closeL $fh;}

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

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

更新:

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

更新 2:

这个问题没有已经在,我无法复制该问题.

这是我使用的代码:

#!/usr/bin/env perl使用严格;使用警告;使用 Win32::LongPath;`cmd/c rd/s/q 测试`;mkdirL('测试')或死$^E";对于我的 $i (200 .. 255) {我的 $dir_name = 'test/' .sprintf(%04d", $i) .('a' x ($i-4));mkdirL($dir_name) 或死$^E";我的 $file_name = "$dir_name/";.('_' x 200).'.文本文件';printf "% 3d\n", 长度 $file_name;openL(\my $fh, '>', $file_name)或死$^E";打印 $fh '你好!'或死$^E";关闭 $fh 或死亡 "$^E";}

输出如下:

C:\…\Temp> perl tt.pl410411412413414415…460461462463464465

我还附上了几个屏幕截图:

现在,我可以报告说Explorer有麻烦导航到任意目录后的 C:\ ... \ TEMP \测试\ 0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

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

大概 GVim 从 命名文件、路径和命名空间

<块引用>

因为您不能在相对路径中使用 "\\?\" 前缀,所以相对路径总是限制为总共 MAX_PATH 个字符.

巧合的是,我的 %TEMP% 目录的路径长度恰好是 33 个字符.加上那个 5(\test 的长度),我们有 38.把它加上 221,我们得到 259.现在,当您向该字符串添加目录分隔符时,您点击了 260.

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

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

#!/usr/bin/env perl使用严格;使用警告;使用 Win32::LongPath;`cmd/c rd/s/q 测试`;mkdirL('测试')或死$^E";对于我的 $i (220 .. 255) {我的 $dir_name = 'test/' .sprintf(%04d", $i) .('a' x ($i-4));mkdirL($dir_name) 或死$^E";我的 $file_name = "$dir_name/";.('_' x 200).'.文本文件';printf "% 3d\n", 长度 $file_name;openL(\my $fh, '>', $file_name)或死$^E";打印 $fh '你好!'或死$^E";关闭 $fh 或死亡 "$^E";openL(\my $in, '<', $file_name)或死$^E";打印 <$in>, "\n";或死$^E";close $in or die "$^E";}

输出:

<前>…459你好!460你好!461你好!462你好!463你好!464你好!465你好!

因为 Win32::LongPath 在内部规范化路径,所以它们遵循要指定扩展长度的路径,请使用 "\\?\代码>前缀"推荐,然后使用 Unicode 版本的 API 调用,例如CreateFileW,openL 不会遇到此类问题.

<块引用>

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

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

另请参阅 "为什么 Perl 系统调用无法调用内部 Windows 命令?"qx{cmd/c rd/s/q test}

I'm experiencing a problem where I cannot create a file under a directory the length of whose name exceeds 220 characters.

The following is a test script that reproduces the observed behavior, at least on my machine:

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;
}

This script will create a _________.....___.txt file under 0220aaaa...aaa/ but not under 0221aaaa...aaa/.

Is there a reason for this problem and how can I change the script so that the *.txt file is created in all directories?

Update:

The script, when run, doesn't die or produce any error messages.

Update 2:

This question does not already have an answer in Why does the 260 character path length limit exist in Windows? As the other question, and its answers, clearly show, this path length limit is a Windows issue, not an NTFS issue. NTFS allows paths with a length of up to 32K characters.

In fact, my test script is able to create directories up to 255 characters (as expected from the NTFS specification), it just seems impossible to store files within a directory when the directory name is longer than 220 characters.

解决方案

TL;DR

The root of your problem is the method by which you are trying to verify that the files have been created. For more information, read the detailed explanation below.

Here are the facts:

  1. perl does not give any errors when using mkdirL or openL calls.
  2. perl can open files created, and read the contents, using openL.

Therefore the problem is due to the fact that whatever tool you are using, is either using ANSI versions of Windows API calls, or specifying relative paths, or a combination of both, and therefore their paths are restricted to 260 characters.

To test this, I ran the script under D:\t. Lo and behold, GVim failed to open a file when $i = 250:

D:\t is four characters, \test is another five. Therefore, 250 + 9 = 259, which hits 260 the moment you add another \.

Using shortpathL

Try this:

#!/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);

You will get:

Therefore, give the short path to any external programs which you cannot rely on to use the Unicode interface.

Long winded explanation

Now that I have had a chance to actually try this on a 64-bit Windows 8.1 system, I cannot replicate the problem.

Here is the code I used:

#!/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";
}

Here is the output:

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

I am also attaching a couple of screenshots:

Now, I can report that Explorer has trouble navigating into any directory after C:\...\Temp\test\0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

and GVim cannot open files in subsequent directories. That is, system 'c:/.../gvim.exe', $file_name; issued from within the script results in

Presumably GVim runs into the following from Naming Files, Paths, and Namespaces

Because you cannot use the "\\?\" prefix with a relative path, relative paths are always limited to a total of MAX_PATH characters.

By coincidence, the length of the path of my %TEMP% directory happens to be 33 characters. Add to that 5 (length of \test), and we have 38. Add that to 221, and we get 259. Now, the moment you add a directory separator to that string, you hit 260.

Which brings me to, what is the length of the full path of the working directory under which you are creating test?

In better news, perl is able to read back everything written:

#!/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";
}

outputs:

…
 459  
Hello!
 460  
Hello!
 461  
Hello!
 462  
Hello!
 463  
Hello!
 464  
Hello!
 465  
Hello!

Because Win32::LongPath internally normalizes paths so they follow the "To specify an extended-length path, use the "\\?\" prefix" recommendation, and then uses the Unicode version of API calls, e.g. CreateFileW, openL does not run into such issues.

In the ANSI version of this function, the name is limited to MAX_PATH characters. To extend this limit to 32,767 wide characters, call the Unicode version of the function and prepend "\\?\" to the path. For more information, see Naming Files, Paths, and Namespaces.

How are you verifying if the files have been correctly created?

Also, see "Why is Perl system call failing to invoke internal Windows command?" for the explanation of qx{cmd /c rd /s /q test}

这篇关于在 Windows 上使用 Perl 在名称长度超过 220 个字符的目录中创建文件的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆