Can Powershell be used to rename files with paths longer than 260 characters?

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.


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 

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.