Wednesday, April 3, 2013

Finding paths that exceed MAX_PATH with Powershell

A lot as been said and written about the infamous MAX_PATH limitation imposed by the Windows API. Still in 2013 this keeps being a major pain for Windows File Server administrators and as such has been over-discussed but never properly solved. My opinion is that, unless a major architectural rework is done at Redmond, it is up to system administrators to keep an eye on their filesystems and try to evite as much as possible to have paths going over that hardcoded maximum.

That's why I want to share the Powershell script I use to find those paths that exceed 260 characters in lenght, being 260 the value of MAX_PATH ever since.
  1. function Get-Longpaths {  
  2. <  
  3. .SYNOPSIS   
  4.   Retrieves a list of the paths that are too long for being managed with get-childitem.  
  5.     
  6. .DESCRIPTION   
  7.   Retrieves a list of the paths that are too long for being managed with get-childitem.  
  8.   An object is returned containing the list of the paths longer then 260 characters.  
  9.   The aim of this function is to help sysadmins to handle paths which could cause the  
  10.   “System.IO.PathTooLongException“ due to their excessive length comared to what is  
  11.   supported by .NET and Windows API.  
  12.   
  13. .PARAMETER Path  
  14.   The parent path that you need to recursively check.  
  15.   
  16. .PARAMETER Csvpath
  17.   The name of the csv log file you optionally want to export your list of long paths to.  
  18.   
  19. .EXAMPLE  
  20.   Get-Longpaths c:\  
  21.   Retrieves long path on the system partition c: and show on the current host.  
  22.   
  23. .EXAMPLE  
  24.   Get-Longpaths f:\documents c:\mylogs\longpathnames.csv -verbose  
  25.   Retrieves long path inside the folder f:\documents and saves the  
  26.   output to a csv file named longpathnames.csv under c:\mylogs\.  
  27.   It also shows additional information on the task being performed.  
  28. #>  
  29. [CmdletBinding()]  
  30.   
  31.     param(  
  32.         [Parameter(Mandatory=$true)]  
  33.         [string] $Path,  
  34.         [Parameter(Mandatory=$false)]  
  35.         [string] $csvlog  
  36.     )  
  37.   
  38. $options = [system.IO.SearchOption]::AllDirectories  
  39. $allfiles = [system.IO.Directory]::GetFiles($path"*",$options)  
  40. $allfolders = [system.IO.Directory]::GetDirectories($path"*",$options)  
  41. $toolongcontainer = @()  
  42.   
  43. foreach($file in $allfiles)  
  44.   {  
  45.   if($file.Length -gt 259)  
  46.     {  
  47.     write-verbose "Adding $file to the list of too long paths"  
  48.     $toolongcontainer  += $file  
  49.     }  
  50.   }  
  51.   
  52. foreach($folder in $allfolders)  
  53.   {  
  54.   if(($folder|out-string).Length -gt 259)  
  55.     {  
  56.     write-verbose "Adding $folder to the list of too long paths"  
  57.     $toolongcontainer  += $folder  
  58.     }  
  59.   }  
  60. if(!($toolongcontainer))  
  61.   {  
  62.   write-verbose "No too long paths found. Good."  
  63.   $toolongcontainer = "No invalid path under $path"  
  64.   }  
  65. if($csvlog)  
  66.   {  
  67.   write-verbose "Exporting output to $csvpath"  
  68.   $toolongcontainer | Export-Csv -Path $csvpath -NoTypeInformation -USECULTURE -ErrorAction Stop   
  69.   }  
  70. return $toolongcontainer  
  71. }  
  72.   
  73. Get-Longpaths C:\longpath -verbose  
As you can see I used the methods GetFiles and GetDirectories of the system.IO.Directory class. In particular GetFiles returns the names of files in a specified directory, while GetDirectories gets the names of the subdirectories. The cool thing with this methods is that, for some mystical reasons explained here and here, they return the full path as a string or arrays and that's all we need.

Then all the objects returned by these two methods are added as strings to a string array named $toolongcontainer for later analysis. The content of this object can be sent to a CSV file as a  record by passing the csv file path as a parameter.

Once you have this information, you can take appropriate actions such as shortening the path, deleting files and folders that exceed MAX_PATH or moving them to a shorter path. This is up to you.

If you need any clarification about this script, feel free to leave a comment and I'll be happy to answer. But remember that the easier way to get help for a function is to dot source the script then use the built-in help function this way: man Get-Longpaths  -full where 'man' is an interesting alias for get-help.

11 comments:

  1. Hi I am new to scripting and would like to know first if can just use the script as it is without making changes, second copy the script into a text file and run it, or just copy and past it on the power shell screen please let me know
    Thanks

    ReplyDelete
    Replies
    1. Hi, yes, you can save it as .ps1 file and then dot-source it and then call the function Get-Longpaths as it is passing a parameter as described in the comment-based help. The parameter is the path to analyze.
      Carlo

      Delete
    2. Hello,

      I am very interested in this function. I am not Powershell proficient, but I do have Powershell 4.0 on a Windows 8.1 laptop.

      Can you explain in detail how to get this function to run on my system or explain what you mean by "dot-source it"?

      Thanks,
      Tony

      Delete
    3. Hello,

      I am very interested in this function. I am not Powershell proficient, but I do have Powershell 4.0 on a Windows 8.1 laptop.

      Can you explain in detail how to get this function to run on my system or explain what you mean by "dot-source it"?

      Thanks,
      Tony

      Delete
  2. I find when I run the script against files I have made with names longer than 260 characters the script runs and reports the filename. When I do the same for a folder I am presented with an error.
    I created a folder structure which has a couple of files over 260 and a couple of folders over 260. When it scans the folders I get

    Exception calling "GetFiles" with "3" argument(s): "Could not find a part of the path
    Exception calling "GetDirectories" with "3" argument(s): "Could not find a part of the path

    Regards

    Richard

    ReplyDelete
  3. This is a great concept for this script and I hope that you can help me get it to work. I've set line 73 as shown below:

    Get-Longpaths "s:\Archived Files" s:\longpathnames.csv -verbose

    Yet I get the following error:

    PS C:\> .\LongFileNames.ps1
    VERBOSE: No too long paths found. Good.
    VERBOSE: Exporting output to
    Export-Csv : Cannot validate argument on parameter 'Path'. The argument is null or empty. Supply an argument that is
    not null or empty and then try the command again.
    At C:\LongFileNames.ps1:68 char:40
    + $toolongcontainer | Export-Csv -Path $csvpath -NoTypeInformation -USECULTURE - ...
    + ~~~~~~~~
    + CategoryInfo : InvalidData: (:) [Export-Csv], ParameterBindingValidationException
    + FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.ExportCsvCommand

    No invalid path under s:\Archived Files
    PS C:\>

    ReplyDelete
    Replies
    1. You can also use Long Path Tool to sort out this problem

      Delete
  4. In the parameters, you defined the option $csvLOG but in the script, you use the variable $csvPATH which is never defined.

    ReplyDelete
  5. Thanks for pointing this out! I shall update the code. Anyhow, did the script was useful to you?
    Carlo

    ReplyDelete
  6. the best alternative solution i think u can use long path tool program. its recomended tool for max_path limitation. :)

    ReplyDelete
  7. When I run this script, I get the following error. How do I change the script to give it full access to the path 'C:\Documents and Setting' ??

    Exception calling "GetFiles" with "3" argument(s): "Access to the path 'C:\Documents and Settings' is denied."
    At C:\Users\admin\Documents\LongPathFinder_Admin.ps1:39 char:1
    + $allfiles = [system.IO.Directory]::GetFiles($path, "*",$options)
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : UnauthorizedAccessException

    Exception calling "GetDirectories" with "3" argument(s): "Access to the path 'C:\Documents and Settings' is denied."
    At C:\Users\adminDocuments\LongPathFinder_Admin.ps1:40 char:1
    + $allfolders = [system.IO.Directory]::GetDirectories($path, "*",$optio ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodInvocationException
    + FullyQualifiedErrorId : UnauthorizedAccessException

    ReplyDelete

Related Posts Plugin for WordPress, Blogger...