Home > Mobile >  Strange behavior of Function call for Path renaming
Strange behavior of Function call for Path renaming

Time:05-05

I am trying to write functions to rename Paths and Files.

I want to add or remove to the directory and file names (excluding the file extensions) at the end or start of the name certain identifiers.

So I wrote these functions:

##################################
# Rename Files and Directories
##################################

function Ensure-String-End ($string, $ending) {
    # Ensure that $string ends with $ending.
    if ($string -match $(-join('', ".*", $ending, '$'))) {
        return $string
    } else {
        return $(-join('', $string, $ending))
    }
}

function Ensure-String-Start ($string, $start) {
    # Ensure that $string starts with the $start string.
    if ($string -match $(-join('', '^', $start, '.*$'))) {
        return $string
    } else {
        return $(-join('', $start, $string))
    }
}

function Remove-String-End ($string, $ending) {
    # Remove $ending string from end of $string
    return $string -replace "$ending$", ''
}

function Remove-String-Start ($string, $start) {
    # Remove $start string from start of $string
    return $string -replace "^$start", ''
}



function Ensure-Filename-End ($filename, $ending) {
    # Similar to Ensure-String-End just for file names - The file extension should stay.
    # But at the end of the filename without extension the $ending should be ensured.
    $path = Get-Item $filename
    return -join('', $path.DirectoryName, '\', $(Ensure-String-End $path.Basename $ending), $path.Extension)
}

function Ensure-Filename-Start ($path, $start) {
    # Filename should start with $start string.
    $path = Get-Item $path
    return -join('', $path.DirectoryName, '\', $(Ensure-String-Start $path.Basename $start), $path.Extension)
}

function Remove-Filename-End ($filename, $ending) {
    # Remove from filename end the $ending (file extension should stay)
    $path = Get-Item $filename
    return -join('', $path.DirectoryName, '\', $(Remove-String-End $path.Basename $ending), $path.Extension) 
}

function Remove-Filename-Start ($path, $start) {
    # Remove from file name's start the $start string. Rest of the path should be invariant.
    $path = Get-Item $path
    return -join('', $path.DirectoryName, '\', $(Remove-String-Start $path.Basename $start), $path.Extension)
}




function Ensure-Directories-Ending ($path, $ending) {
    # Make directories end with $ending and rename (`mv`) the directories.
    Get-ChildItem -Path $path -Directory | %{$_.FullName} |
        ForEach-Object { 
            $new_name = Ensure-String-End $_ $ending
            if ($new_name -ne $_) {
                echo "Renaming $_ to $new_name"
                Rename-Item -Path $_ -NewName $new_name
            }
        }
}

function Ensure-Files-Ending ($path, $ending) {    
    # `mv` the file names, ensuring they end with $ending - while file extension is kept.
    Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
        ForEach-Object {
            $new_name = Ensure-Filename-End $_ $ending
            if ($new_name -ne $_) {
                echo "Renaming $_ to $new_name"
                Rename-Item -Path $_ -NewName $new_name
            }
        }
}

function Ensure-Directories-and-Files-Ending ($path, $ending) {
    # Recursively add to all directory and file names the $ending if they don't end already by it.
    Ensure-Directories-Ending $path $ending
    Ensure-Files-Ending $path $ending
}

function Remove-Directories-Ending ($path, $ending) {
    # Rename directories so that if they end with $ending this $ending is removed.
    Get-ChildItem -Path $path -Directory | 
        ForEach-Object {
            $dir_path = %{$_.FullName}
            $new_name = Remove-String-End $dir_path $ending
            if ($new_name -ne $dir_path) {
                echo "Renaming $dir_path to $new_name"
                Rename-Item -Path $dir_path -NewName $new_name
            }
        }
}

function Remove-Files-Ending ($path, $ending) {
    # Remove $ending from File names - rename them in this system.
    Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
        ForEach-Object {
            $new_name = Remove-Filename-End $_ $ending
            if ($new_name -ne $_) {
                echo "Renaming $_ to $new_name"
                Rename-Item -Path $_ -NewName $new_name
            }
        }
}

function Remove-Directories-and-Files-Ending ($path, $ending) {
    # Recursively remove from ending of all Folders and Files' names the $ending string.
    $(Remove-Directories-Ending $path $ending)
    $(Remove-Files-Ending $path $ending)
}

Generate a test folder:

New-Item .\Desktop\test\a -type Directory 
New-Item .\Desktop\test\b -type Directory 
New-Item .\Desktop\test\c -type Directory
New-Item   .\Desktop\test\a\a.txt -type File
New-Item   .\Desktop\test\a\b.txt -type File
New-Item   .\Desktop\test\a\c.txt -type File
New-Item   .\Desktop\test\b\a.txt -type File
New-Item   .\Desktop\test\b\b.txt -type File
New-Item   .\Desktop\test\b\c.txt -type File
New-Item   .\Desktop\test\c\a.txt -type File
New-Item   .\Desktop\test\c\b.txt -type File
New-Item   .\Desktop\test\c\c.txt -type File

Add to each end of folders and files a certain ending:

Ensure-Directories-and-Files-Ending $HOME\Desktop\test "_test"

And remove them again:

Remove-Directories-and-Files-Ending $HOME\Desktop\test "_test"

there appear:

Rename-Item : Cannot rename the specified target, because it represents a path or device name.

Errors/Warnings.

How can I avoid them?

CodePudding user response:

The Rename-Item cmdlet's -NewName parameter truly only accepts a new name for the input file-system item, not a path:

  • By design and invariably, Rename-Item renames a file or directory (or other PowerShell provider item) in its current location.

  • If, by contrast, you want to rename and also move the item to a different location, you must use the Move-Item cmdlet.


You can easily provoke the error as follows:

PS> Get-Item $PROFILE | Rename-Item -NewName "$HOME\NewName" -WhatIf
Rename-Item: Cannot rename the specified target, because it represents a path or device name.

Note that, as a courtesy, Rename-Item does accept a path in two cases:

  • If the -NewName argument is prefixed by verbatim relative path .\ (or ./).

    • For instance, the following two calls are equivalent:

      Rename-Item -LiteralPath c:\path\to\foo.txt -NewName bar.txt
      Rename-Item -LiteralPath c:\path\to\foo.txt -NewName .\bar.txt
      
    • Do note that . in this context does not refer to the current directory, but to that of the input file.

  • Curiously, the input item's current, full path is also accepted, but, given that you cannot rename an item to itself, this has the following effect:

    • For input items that are files, the call is a quiet no-op.
    • For directories, you get an error that states, "Rename-Item: Source and destination path must be different."
    • This surprising inconsistency is the subject of GitHub issue #14903.
  • Related