我有一个构build过程的一部分,在Windows中创build一个可怕的长path。 这不是我的错。 这是几个目录深,没有目录名称是exception长; 他们只是很长,足以超过MAX_PATH
(260个字符)。 在这些名称中,我没有使用除ASCII以外的其他任何内容。
最大的问题是,在dist
目标的时候, Module :: Build的内核会发生爆炸,尽pipe我认为构build系统并不重要,因为它们会创build相同的目录。
使用File::Path
创build其中一个超长的目录会失败:
use File::Path qw( make_path ); make_path( 'C:\\.....' ); # fails if path is over 260 chars
同样,一旦绝对path超过MAX_PATH
,手动构build每个目录级别将失败。
这不是新的,不是Perl的错,Microsoft将它logging在命名文件,path和命名空间中 。 他们的修复build议在访问Unicode文件名API的任何path前加\\?\
。 但是,这似乎并不是Perl脚本的完整解决scheme,因为它仍然失败:
use File::Path qw( make_path ); make_path( '\\\\?\\C:\\.....' ); # still fails if path is over MAX_PATH, works otherwise
这可能是因为make_path
将其参数拉开,然后一次遍历目录,所以\\?\
只适用于MAX_PATH
的顶层。
我挖了一个错误报告给ActiveState ,这意味着还有其他的东西需要我去修正Unicode文件名,而Jan Dubois给出了一些更多的细节:Windows 2K / XP上的“long”文件名 ,虽然我不知道它是否适用(并且是非常古老的)。 perlrun提到这个用途是-C
开关的工作,但显然这部分被放弃了。 perl RT队列具有更新的错误60888:Win32:支持文件名中的完整unicode(使用Wide-system调用) 。
宫川注意到一些Unicode文件名问题和Win32API :: File没有特别提及长path。 但是, Win32API :: File CPAN Forum条目似乎只是表示恐惧,这会导致愤怒,从而导致讨厌,等等。 在Perlmonks文章中有一个例子如何在Windows中使用Unicode(UTF16-LE)文件名统计文件? 。 看来Win32::CreateDirectory
就是答案,下一次我将在Windows机器旁边尝试一下。
那么,假设我可以创build长path的path。 现在我必须教Module / Build,也许还有其他的东西来处理它。 如果Win32::GetANSIPathName()
完成它所说的话,monkeypatches可能会很容易。
以下脚本起作用:它将一个字符串写入一个具有较长路径的目录中的文件,并且能够读回相同的字符串。 (成功运行不会产生控制台输出)。 我也做了一个笨重的努力来覆盖open
。
#!/usr/bin/perl use strict; use warnings; use Carp; use Encode qw( encode ); use Symbol; use Win32; use Win32API::File qw( CreateFileW OsFHandleOpen FILE_GENERIC_READ FILE_GENERIC_WRITE OPEN_EXISTING CREATE_ALWAYS FILE_SHARE_READ ); use Win32::API; use File::Spec::Functions qw(catfile); Win32::API->Import( coreel32 => qq{BOOL CreateDirectoryW(LPWSTR lpPathNameW, VOID *p)} ); my %modes = ( '<' => { access => FILE_GENERIC_READ, create => OPEN_EXISTING, mode => 'r', }, '>' => { access => FILE_GENERIC_WRITE, create => CREATE_ALWAYS, mode => 'w', }, # and the rest ... ); use ex::override open => sub(*;$@) { $_[0] = gensym; my %mode = %{ $modes{$_[1]} }; my $os_fh = CreateFileW( encode('UCS-2le', "$_[2]\0"), $mode{access}, FILE_SHARE_READ, [], $mode{create}, 0, [], ) or do {$! = $^E; return }; OsFHandleOpen($_[0], $os_fh, $mode{mode}) or return; return 1; }; my $path = '\\\\?\\' . Win32::GetLongPathName($ENV{TEMP}); my @comps = ('0123456789') x 30; my $dir = mk_long_dir($path, \@comps); my $file = 'test.txt'; my $str = "This is a test\n"; write_test_file($dir, $file, $str); $str eq read_test_file($dir, $file) or die "Read failure\n"; sub write_test_file { my ($dir, $file, $str) = @_, my $path = catfile $dir, $file; open my $fh, '>', $path or croak "Cannot open '$path':$!"; print $fh $str or die "Cannot print: $!"; close $fh or die "Cannot close: $!"; return; } sub read_test_file { my ($dir, $file) = @_, my $path = catfile $dir, $file; open my $fh, '<', $path or croak "Cannot open '$path': $!"; my $contents = do { local $/; <$fh> }; close $fh or die "Cannot close: $!"; return $contents; } sub mk_long_dir { my ($path, $comps) = @_; for my $comp ( @$comps ) { $path = catfile $path, $comp; my $ucs_path = encode('UCS-2le', "$path\0"); CreateDirectoryW($ucs_path, undef) or croak "Failed to create directory: '$path': $^E"; } return $path; }
使用带有内置open
Win32::GetANSIPathName()
不起作用:返回的路径太长。
查看失败实验的编辑历史记录。
下面的代码实际上创建了相当深的(长度超过260个字符)的目录结构。 至少在我的机器上:
use Win32::API; $cd = Win32::API->new('kernel32', 'CreateDirectoryW', 'PP', 'N'); $dir = '\\\\?\\c:\\!experiments'; $res = 1; do { print 'path length: ' . length($dir) . "\n"; $dirname = pack('S*', unpack('C*', "$dir\0")); #dirty way to produce UTF-16LE string $res = $cd->Call($dirname, 0); print "$res\n"; $dir .= '\\abcde'; } while ( $res );
我明白这不是解决您的具体问题。 然而,有很多情况下,能够将一个很长的路径映射到一个驱动器盘符将允许人们避开这个问题,因此在处理非常长的路径名时是有用的,而不需要涉及大量的Windows特定的代码和文档。
尽管我努力了解如何做到这一点,但我会推荐使用SUBST
。 Win32 :: FileOp提供了Unsubst
和Unsubst
。 然后,您可以将顶级工作目录映射到一个未使用的驱动器盘符(可以使用Substed
)。 我会开始与Z
检查和反向工作。
或者,你可以退出,调用没有参数的subst
实用程序来获取当前替换列表,选择一个不存在的列表。
这一切都不是完全安全的,因为替换在构建过程中可能会发生变化。
这应该是一个评论,但在注释中发布代码是没有用的。
UNC路径也不起作用:
C:\>网络份额 perlbuild e:\ home \ src
#!/usr/bin/perl use strict; use warnings; use File::Path qw(make_path); use File::Slurp; use Path::Class; my $top = dir('//Computer/perlbuild'); my @comps = ('0123456789') x 30; my $path = dir($top, @comps); make_path $path, { verbose => 1 }; my $file = file($path, 'test.txt'); write_file "$file" => 'This is a test'; print read_file "$file";
结果:
mkdir \\ Computer \ perlbuild \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456 789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 0123456789 \ 01 23456789 \ 0123456789:没有这样的文件或目录; 文件名或扩展名也是如此 长在C:\ Temp \ k.pl 15行
我有三个想法,所有这些都是黑客:
从一些简短的目录名称(C:\ data_directory \ a \ b \ c \ d \ 4 \ 5 \ 6 \ …)开始,然后重命名这些目录(当然首先从最深的目录开始)。
创建一个中等长度的路径的Windows快捷方式,并从那里创建文件和子目录? (或者安装Cygwin并使用符号链接?)
在短名称的目录中创建所需的文件,zip / tar它们,然后将它们解压到具有较长名称的目录中。 或者“手动”创建zip / tar文件并将其解压到所需的位置。