路径重命名函数调用的奇怪行为



我正在尝试编写重命名路径和文件的函数。

我想在目录和文件名(不包括文件扩展名)的末尾或开头添加或删除某些标识符。

所以我写了这些函数:
##################################
# 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 .Desktoptesta -type Directory 
New-Item .Desktoptestb -type Directory 
New-Item .Desktoptestc -type Directory
New-Item   .Desktoptestaa.txt -type File
New-Item   .Desktoptestab.txt -type File
New-Item   .Desktoptestac.txt -type File
New-Item   .Desktoptestba.txt -type File
New-Item   .Desktoptestbb.txt -type File
New-Item   .Desktoptestbc.txt -type File
New-Item   .Desktoptestca.txt -type File
New-Item   .Desktoptestcb.txt -type File
New-Item   .Desktoptestcc.txt -type File

在文件夹和文件的每一个末端添加一个特定的结尾:

Ensure-Directories-and-Files-Ending $HOMEDesktoptest "_test"

并再次删除它们:

Remove-Directories-and-Files-Ending $HOMEDesktoptest "_test"

出现:

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

错误/警告。

我怎样才能避免它们?

我的最终解决方案

谢谢大家的意见。考虑到它们,我得出结论,为路径处理编写自己的小函数将对我的问题有所帮助。


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 .Desktoptest "_test"
# Remove-End-All .Desktoptest "_test"
# Ensure-Start-All .Desktoptest "test_"
# Remove-Start-All .Desktoptest "test_"

Rename-Itemcmdlet的-NewName参数确实只接受输入文件系统项的新名称,而不接受路径:

  • 根据设计,Rename-Item将在当前位置重命名文件或目录(或其他PowerShell提供程序项)

  • 相反,如果您想重命名并将项目移动到不同的位置,则必须使用Move-Itemcmdlet .


您可以很容易地触发以下错误:

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

注意,出于礼貌,Rename-Item在两种情况下接受路径:

  • 如果-NewName参数前缀为逐字相对路径.(或./)。

    • 例如,以下两个调用是等效的:

      Rename-Item -LiteralPath c:pathtofoo.txt -NewName bar.txt
      Rename-Item -LiteralPath c:pathtofoo.txt -NewName .bar.txt
      

    • 请注意,这里的.并不是指当前目录,而是指输入文件的目录。

  • 奇怪的是,输入项的当前完整路径也被接受,但是,考虑到您不能将项重命名为其本身,这有以下效果:

    • 对于文件的输入项,调用是安静的不操作
    • 对于目录,你会得到一个错误声明,"Rename-Item: Source and destination path must be different.">
    • 这个令人惊讶的不一致是GitHub问题#14903的主题。

相关内容

  • 没有找到相关文章

最新更新