3

I have hundreds of files with paths longer than 260 characters that I wish to rename. Using Powershell, I can find the long names using cmd /c dir /s /b |? {$_.length -gt 260}, but the actual cmdlets don't support long filenames: Get-Item would return

"cannot find path [long path] because it does not exist"

I read that a module called NTFSSecurity can work with long paths, but so far I only know that it can list the files using a cmdlet called Get-ChildItem2. I still don't know a way to actually rename the files this way.

Outside of Powershell, I found a tool named TLPD that can list the files, but that's all.

So far I've only been able to deal with it by manually renaming the parent folders so that the path becomes short enough to be accessed (I may be able to emulate this process using Powershell -- but I'd appreciate an easier way)

Edit: to clarify, I'd like to keep the files instead of deleting them as much as possible.

user
  • 163

4 Answers4

2

I wrote a script that worked. Thanks to jftuga for the subst suggestion.

# This script searches through the search_path, finds file paths that are longer than total_path_length_threshold, and truncates the filename

### user settings ###

$test_run = $true                   # set to $false to start renaming for real
$search_path = "C:\my_folder\"      # the path to search for long names
$total_path_length_threshold = 260  # the length threshold to test
$characters_to_truncate = 5         # the number of characters to strip from the end of long filenames (a new identifier will also be added)
$log_file_path = "c:\my_folder\long_filename_log.txt"  # log file location

$virtual_drive = "v:"               # used for temporary processing using subst. Must look like "v:" and not like "v:\"

### end of user settings ###

Out-File $log_file_path
"Test run = $test_run" | Tee-Object $log_file_path -Append
"search_path = $search_path " | Tee-Object $log_file_path -Append
"total_path_length_threshold = $total_path_length_threshold " | Tee-Object $log_file_path -Append
"characters_to_truncate = $characters_to_truncate " | Tee-Object $log_file_path -Append

$counter = 0  # counter for creating new unique filenames
$collection = cmd /c dir $search_path /s /b |? {$_.length -gt $total_path_length_threshold }
$count_paths = ($collection | measure).count - 1

foreach ($path in $collection) {
    echo "found long path: $path"

    # get the parent's path
    $parent_path = Split-path -path $path

    # mount the parent to a virtual drive
    subst $virtual_drive $parent_path

    # get leaf element
    $leaf = split-path -leaf $path

    # make new short path
    $short_virtual_path = join-path $virtual_drive $leaf

    # get the long-name item 
    $item = Get-Item -LiteralPath $short_virtual_path

    if ($item -is [System.IO.fileinfo]) {
        # log long filename path
        "item $counter : Long filename path: $path" | Out-File $log_file_path -Append

        $filename = $item.name  #get the full filename

        # get filename extension
        $filename_extension = $item.Extension

        # get filename without extension:
        $basename = $item.BaseName    
        $basename_length = $basename.length 

        # give the new filename a unique index, to avoid duplicate filenames
        $new_index = "X" + $counter + "X"
        # the actual number of characters to cut is characters_to_truncate + new_index length
        $adjusted_characters_to_truncate = $characters_to_truncate + $new_index.length 

        if ( $basename_length -gt $adjusted_characters_to_truncate ) {
            $length_to_use = $basename_length - $adjusted_characters_to_truncate

            # shorten the filename by truncating it by specified num of char
            $new_filename = $basename.substring(0, $length_to_use ) + $new_index + $filename_extension

            # log the new filename path
            $new_path = $parent_path + $new_filename
            "item $counter : new filename path: $new_path " | Out-File $log_file_path -Append

            if (!$test_run) {
                # rename the file

                Rename-Item -LiteralPath $short_virtual_path $new_filename
            }

        }

    }

    $counter = $counter + 1

    #unmount the virtual drive
    subst v: /d
}

"Found $count_paths paths." | echo 
user
  • 163
1

This is how you can delete folders and files with really long path names, albeit not in powershell.

mkdir c:\alfa\bravo\charlie\delta\echo\foxtrot\golf\hotel\india\julliett\kilo\lima\mike\november\oscar\papa\quebec\romeo\sierra\tango\uniform\victor\whiskey\xray\yankee\zulu

join j: c:\alfa\bravo\charlie\delta\echo\foxtrot\golf\hotel\india\julliett
j:
subst k: j:\kilo\lima\mike\november\oscar\papa\quebec\romeo\sierra
k:
subst l: k:\tango\uniform\victor\whiskey
l:
rd /s/q xray
k:
rd /s/q tango
j:
rd /s/q kilo
c:
rd /s/q alfa

subst l: /d
subst k: /d
subst j: /d

The overall concept is to chain multiple subst commands so that they are nesting deep into the folder structure. You may have to go about 6 drive letters deep. Then you can start deleting folders from the the deepest level in and then traverse your way back up & out of the path. Once done, you can remove the drive letters created with the subst command.

jftuga
  • 3,265
0

there are is this cool node utility that will do the job called rimraf.If you have node installed already just install it via npm

npm install -g rimraf
Usage: rimraf dir

Also you could use robocopof which is able to create paths longer than the limit of 260 characters and also able to delete them

see Robocopy Page

Install it from Microsoft Page

0

To delete the paths using robocopy you can use the /purge parameter to an empty directory.

CMD Example:
Robocopy.exe C:\temp\SourceDir C:\Temp\EmptyDir /Purge

Here the SourceDir being the Directory to be deleted

This can be paired with PowerShell quite easily

PowerShell Example:

$Paths = Get-Content C:\Temp\DeleteDirectories
ForEach($Path in $Paths){
   Robocopy.exe $Path C:\Temp\EmptyDir /Purge
           }

This will loop through the Paths you provide in the C:\Temp\DeleteDirectories location and delete them.

Hope this helps!