Creating a file using Perl on Windows in a directory longer than 220 characters

I have a problem where I cannot create a file under a directory that is longer than 220 characters.

Below 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 the _________.....___.txt 0220aaaa...aaa/ file under 0220aaaa...aaa/ , but not under 0221aaaa...aaa/ .

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

Update:

The script does not die at startup and does not generate any error messages.

Update 2:

This question does not yet have an answer. Why is there a path length limit of 260 characters on Windows? Since another question and its answers clearly show, this path length limitation is a Windows problem, not an NTFS problem. NTFS allows paths up to 32 thousand characters long.

In fact, my test script is capable of creating directories up to 255 characters (as expected from the NTFS specification), it is simply not possible to store files in a directory when the directory name is longer than 220 characters.

+5
source share
1 answer

TL DR

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

Here are the facts:

  • perl does not cause errors when using mkdirL or openL .
  • perl can open the created files and read the contents using openL .

Therefore, the problem is that any tool that you use either uses the ANSI versions of the Windows API calls, or indicates relative paths, or a combination of them, so their paths are limited to 260 characters.

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

With run under D: \ t, GVim fails to open file at $ i = 250

D:\t - four characters, \test - five more. Therefore, 250 + 9 = 259 , which reaches 260 when another one is added.

Using shortpathL

Try the following:

 #!/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'll get:

Notepad can open when given short path

So give a shortcut to any external programs you cannot rely on to use the Unicode interface.

Long term explanation

Now that I had the opportunity to 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 result:

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

I also attach a couple of screenshots:

Explorer window showing the directories created

GVim Window showing one of the files

I can now announce that the Explorer has problems with navigation in any directory after the C:\...\Temp\test\0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa \ 0220aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa

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

GVim having trouble with 221 character directory name

GVim Presumably Works with Naming Files , Paths , and Namespaces

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

Coincidentally, the path length of my %TEMP% directory is 33 characters. Add to this 5 (length \test ), and we have 38. Add this to 221, and we get 259 . Now, when you add the directory separator to this line, you press 260 .

What leads me to , what is the length of the full working directory path under which you create test ?

In the best news, perl can read 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 "To specify an extended path, use the prefix "\\?\" " And then use the version of Unicode API calls, for example. CreateFileW , openL does not face such problems.

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 version of the Unicode function and add "\\?\" To the path. See Naming Files, Paths, and Namespaces for more information.

How do you verify the correct creation of files?

Also see β€œ Why can't a Perl system call invoke an internal Windows command? ” For an explanation of qx{cmd /c rd /s /q test}

+4
source

Source: https://habr.com/ru/post/1212786/


All Articles