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.