路径重命名的函数调用的奇怪行为
Strange behavior of Function call for Path renaming
我正在尝试编写函数来重命名路径和文件。
我想在名称的末尾或开头添加或删除目录和文件名(不包括文件扩展名)某些标识符。
所以我写了这些函数:
##################################
# 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)
}
生成测试文件夹:
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
在文件夹和文件的每一端添加一个特定的结尾:
Ensure-Directories-and-Files-Ending $HOME\Desktop\test "_test"
并再次删除它们:
Remove-Directories-and-Files-Ending $HOME\Desktop\test "_test"
出现了:
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
Errors/Warnings.
我怎样才能避免它们?
我的最终解决方案
谢谢大家的意见。考虑到它们,我得出的结论是,编写自己的路径处理小函数对我的事情会有所帮助。
function PathLast ($path) {
return $path.split('\')[-1]
}
function PathExt ($path) {
$last = PathLast $path
if ($last.Contains('.')) {
return ".$($last.split('.')[-1])"
} else {
return ''
}
}
function PathBase ($path) {
$last = PathLast $path
return $last -replace "$(PathExt $path)$", ''
}
function PathDir ($path) {
$last = PathLast $path
return $($path -replace "$last$", '') -replace '\$', ''
}
# Renaming Paths should test, whether the absolute names are identical
# if identical, no renaming is performed.
function Rename-Path ($path, $new_path) {
if ($path -ne $new_path) {
Rename-Item -Path $path -NewName $new_path
}
}
# Using those, the renaming functions were written.
# Each of them work both for files and directories/folders as well.
function Ensure-End ($path, $ending) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
echo "$dir\$base$ext"
if (-not $base.EndsWith($ending)) {
Rename-Path $path "$dir\$base$ending$ext"
}
}
function Ensure-Start ($path, $start) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
if (-not $base.StartsWith($start)) {
Rename-Path $path "$dir\$start$base$ext"
}
}
function Remove-End ($path, $ending) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
if ($base.EndsWith($ending)) {
Rename-Path $path "$dir\$($base -replace "$ending$", '')$ext"
}
}
function Remove-Start ($path, $start) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
if ($base.StartsWith($start)) {
Rename-Path $path "$dir\$($base -replace "^$start", '')$ext"
}
}
# The following functions are like the previous ones,
# just recursively applying the renamings on all children
# in the path-tree.
function Ensure-End-All ($path, $ending) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Ensure-End $_ $ending
}
}
function Ensure-Start-All ($path, $start) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Ensure-Start $_ $start
}
}
function Remove-End-All ($path, $ending) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Remove-End $_ $ending
}
}
function Remove-Start-All ($path, $start) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Remove-Start $_ $start
}
}
# Ensure-End-All .\Desktop\test "_test"
# Remove-End-All .\Desktop\test "_test"
# Ensure-Start-All .\Desktop\test "test_"
# Remove-Start-All .\Desktop\test "test_"
Rename-Item
cmdlet 的 -NewName
参数实际上只接受新的 name输入 file-system 项目,而不是 path:
根据设计,Rename-Item
会在其当前位置重命名文件或目录(或其他 PowerShell provider 项目) .
相反,如果您想重命名 并将项目移动到不同的位置,则必须使用 Move-Item
命令。
您很容易引发如下错误:
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.
请注意,出于礼貌,Rename-Item
在两种情况下接受路径:
如果-NewName
参数以逐字相对路径为前缀.\
(或./
)。
例如,下面两个调用是等价的:
Rename-Item -LiteralPath c:\path\to\foo.txt -NewName bar.txt
Rename-Item -LiteralPath c:\path\to\foo.txt -NewName .\bar.txt
请注意,在此上下文中 .
不是 指的是 当前 目录,但是到输入文件的那个。
奇怪的是,输入项的 当前完整路径 也被接受,但是,鉴于您不能将项重命名为自身,这具有以下效果:
- 对于 文件 的输入项,调用是 安静 no-op.
- 对于目录,您会收到一个错误,指出“Rename-Item:源路径和目标路径必须不同。 “
- 这种令人惊讶的不一致是 GitHub issue #14903 的主题。
我正在尝试编写函数来重命名路径和文件。
我想在名称的末尾或开头添加或删除目录和文件名(不包括文件扩展名)某些标识符。
所以我写了这些函数:
##################################
# 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)
}
生成测试文件夹:
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
在文件夹和文件的每一端添加一个特定的结尾:
Ensure-Directories-and-Files-Ending $HOME\Desktop\test "_test"
并再次删除它们:
Remove-Directories-and-Files-Ending $HOME\Desktop\test "_test"
出现了:
Rename-Item : Cannot rename the specified target, because it represents a path or device name.
Errors/Warnings.
我怎样才能避免它们?
我的最终解决方案
谢谢大家的意见。考虑到它们,我得出的结论是,编写自己的路径处理小函数对我的事情会有所帮助。
function PathLast ($path) {
return $path.split('\')[-1]
}
function PathExt ($path) {
$last = PathLast $path
if ($last.Contains('.')) {
return ".$($last.split('.')[-1])"
} else {
return ''
}
}
function PathBase ($path) {
$last = PathLast $path
return $last -replace "$(PathExt $path)$", ''
}
function PathDir ($path) {
$last = PathLast $path
return $($path -replace "$last$", '') -replace '\$', ''
}
# Renaming Paths should test, whether the absolute names are identical
# if identical, no renaming is performed.
function Rename-Path ($path, $new_path) {
if ($path -ne $new_path) {
Rename-Item -Path $path -NewName $new_path
}
}
# Using those, the renaming functions were written.
# Each of them work both for files and directories/folders as well.
function Ensure-End ($path, $ending) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
echo "$dir\$base$ext"
if (-not $base.EndsWith($ending)) {
Rename-Path $path "$dir\$base$ending$ext"
}
}
function Ensure-Start ($path, $start) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
if (-not $base.StartsWith($start)) {
Rename-Path $path "$dir\$start$base$ext"
}
}
function Remove-End ($path, $ending) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
if ($base.EndsWith($ending)) {
Rename-Path $path "$dir\$($base -replace "$ending$", '')$ext"
}
}
function Remove-Start ($path, $start) {
$dir = PathDir $path
$base = PathBase $path
$ext = PathExt $path
if ($base.StartsWith($start)) {
Rename-Path $path "$dir\$($base -replace "^$start", '')$ext"
}
}
# The following functions are like the previous ones,
# just recursively applying the renamings on all children
# in the path-tree.
function Ensure-End-All ($path, $ending) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Ensure-End $_ $ending
}
}
function Ensure-Start-All ($path, $start) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Ensure-Start $_ $start
}
}
function Remove-End-All ($path, $ending) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Remove-End $_ $ending
}
}
function Remove-Start-All ($path, $start) {
Get-ChildItem -Path $path -Recurse | %{$_.FullName} |
ForEach-Object {
Remove-Start $_ $start
}
}
# Ensure-End-All .\Desktop\test "_test"
# Remove-End-All .\Desktop\test "_test"
# Ensure-Start-All .\Desktop\test "test_"
# Remove-Start-All .\Desktop\test "test_"
Rename-Item
cmdlet 的 -NewName
参数实际上只接受新的 name输入 file-system 项目,而不是 path:
根据设计,
Rename-Item
会在其当前位置重命名文件或目录(或其他 PowerShell provider 项目) .相反,如果您想重命名 并将项目移动到不同的位置,则必须使用
Move-Item
命令。
您很容易引发如下错误:
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.
请注意,出于礼貌,Rename-Item
在两种情况下接受路径:
如果
-NewName
参数以逐字相对路径为前缀.\
(或./
)。例如,下面两个调用是等价的:
Rename-Item -LiteralPath c:\path\to\foo.txt -NewName bar.txt Rename-Item -LiteralPath c:\path\to\foo.txt -NewName .\bar.txt
请注意,在此上下文中
.
不是 指的是 当前 目录,但是到输入文件的那个。
奇怪的是,输入项的 当前完整路径 也被接受,但是,鉴于您不能将项重命名为自身,这具有以下效果:
- 对于 文件 的输入项,调用是 安静 no-op.
- 对于目录,您会收到一个错误,指出“Rename-Item:源路径和目标路径必须不同。 “
- 这种令人惊讶的不一致是 GitHub issue #14903 的主题。