如何在Powershell管道中多次使用$u(子字符串方法)

如何在Powershell管道中多次使用$u(子字符串方法),powershell,Powershell,我正在尝试编写一个自动化程序,这样我就可以按如下所述提供输入并获得指定的输出,我正在使用Powershell来完成这项工作 INPUT : Song name in the format given below Artist Name - Song Name.mp3 OUTPUT : Rename the song file in the format given below Song Name - Artist Name.Mp3 我知道有OTS工具可以实现这一点,但我正在尝

我正在尝试编写一个自动化程序,这样我就可以按如下所述提供输入并获得指定的输出,我正在使用Powershell来完成这项工作

INPUT : Song name in the format given below
    Artist Name - Song Name.mp3

OUTPUT : Rename the song file in the format given below
    Song Name - Artist Name.Mp3
我知道有OTS工具可以实现这一点,但我正在尝试使用PowerShell作为更大解决方案的一部分来实现ddo

我使用单行PowerShell cmdlet获取项目,然后重命名它们。 所以我试了一下:

获取ChildItem*.mp3 |重命名Item-NewName{$\.Name.Substring$\.Name.IndexOf-+2,$\.Name.IndexOf.+-+$\.Name.Substring0,$\.Name.IndexOf-+mp3} 这就是问题所在。子字符串部分中的第二个IndexOf获得空值,因为它在同一子字符串操作中被使用了两次

$_.Name.Substring($_.Name.IndexOf("-")+2,  $_.Name.IndexOf("."))
这似乎与PowerShell有关。以下面几行为例:

Get-ChildItem *.mp3 | Select-Object {$_.Name.Substring($_.Name.IndexOf("-")+2)}
Get-ChildItem *.mp3 | Select-Object {$_.Name.Substring($_.Name.IndexOf(".mp3"))}
当它们分开时,Ps为每次运行提供一个输出。但是,如果同时运行两个cmdlet,则只有第一行给出输出,第二行返回NULL

如何获得以下逻辑的输出我在这里进行选择只是为了排除故障,因为排除故障时我不想实际重命名

Get-ChildItem *.mp3 | Select-Object {$_.Name.Substring($_.Name.IndexOf("-")+2, $_.Name.IndexOf(".mp3"))}

我可以使用变量和连接字符串,但如果可能的话,我会在不编写自定义代码/模块的情况下将进程保持在一行中。

在以您尝试的方式重命名这些文件时,我也没有成功

我能够用更多的代码来实现这一点。如果您确实想:

Expanded code for ease of reading:
#Set location of your mp3's 
Set-Location "\\FileServer\TestShare"

#Create your array pf MP3s
$mp3s = Get-ChildItem *.mp3

Clear-Host
#loop through the tracks and rename them
foreach($mp3 in $mp3s)
{
   #Display original name
   Write-Host "`nOldName: " $mp3.Name -ForegroundColor Yellow

   #Split the old name into individual pieces
   $split = $mp3.Name.Split("-.")

   #Remove excess spaces from string
   $split = $split -replace (' ','')

   #Create the new name
   $unSplit = "$($split[1]) - $($split[0]).$($split[2])"

   #Display the new name
   Write-Host "NewName: "$unSplit -ForegroundColor Green   

   #Actually Rename the file
   Rename-Item -Path $mp3.Name -NewName $unSplit
}
输出:

OldName:  Farbrikam - MajorRelease1.mp3
NewName:  MajorRelease1 - Farbrikam.mp3

OldName:  Farbrikam - MajorRelease2.mp3
NewName:  MajorRelease2 - Farbrikam.mp3
堵塞的1号班轮:

$mp3s = Get-ChildItem *.mp3;foreach($mp3 in $mp3s) {Write-Host "`nOldName: " $mp3.Name -ForegroundColor Yellow;$split = $mp3.Name.Split("-.");$split = $split -replace (' ','');$unSplit = "$($split[1]) - $($split[0]).$($split[2])";Write-Host "NewName: "$unSplit -ForegroundColor Green;Rename-Item -Path $mp3.Name -NewName $unSplit}
显然,您可以从代码中删除写主机,因为我正在使用它们检查名称结构,以确保它是我想要的

此代码假定所有文件都将以以下格式命名: 艺人名称-歌曲名称.mp3

示例:

Korn - Blind.mp3

Rob Zombie - Dragula.mp3

Luinz - I got 5 on it.mp3

希望这有帮助

问题的根源是子字符串表达式。如果你把它从管道中断开一会儿,你会发现有一个错误正在被吞没

PS>$s=我的艺术家-我的歌曲.mp3 PS>$s.Substring$s.IndexOf-+2$s.IndexOf。 使用2个参数调用子字符串时出现异常:索引和长度必须引用字符串中的位置。 参数名称:长度 第1行字符:1 +$s.Substring$s.IndexOf-+2$s.IndexOf。 + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +CategoryInfo:NotSpecified::[],ParentContainerErrorRecordException +FullyQualifiedErrorId:ArgumentOutOfRangeException 问题是它需要一个startIndex和length参数,但您试图给它一个startIndex和endIndex。结果是,由于在管道中抛出异常,您的新名称是一个空字符串

你可以用正则表达式来解决这个问题,比如:

PS>$s=我的艺术家-我的歌曲.mp3 PS>$matches=[regex]::匹配$s、.-?.mp3 PS>$newname=$matches.Groups[song].Value+-+$matches.Groups[artist].Value+.mp3 PS>$newname 我的歌-我的艺术家.mp3 如果你把它放回你的管道,你会得到:

获取ChildItem*.mp3 |重命名项-NewName{ $matches=[regex]::Match$\名称、.-?.*.mp3 $matches.Groups[song].Value+-+$matches.Groups[artist].Value+.mp3 } 但是,在您承诺重命名整个音乐库之前,请先玩一下Select Object:-

让我补充一下,这很好地解释了您尝试的问题,并提供了一个优雅的基于正则表达式的解决方案

关于本问题和本声明的标题:

子字符串部分中的第二个IndexOf获得空值,因为它在同一子字符串操作中被使用了两次

$_.Name.Substring($_.Name.IndexOf("-")+2,  $_.Name.IndexOf("."))
对于可以在脚本块中使用$的次数没有限制,如下例所示:

PS> 'one' | ForEach-Object { $_, $_.Substring(1), $_.SubString(2) -join '-' }
one-ne-e
从技术上讲,可以修改,因此也可以放弃$\的值,但这应该避免,因为所有的修改都应该避免

字符串方法(如.Substring)从根本上返回输入字符串的修改副本-它们从不在适当的位置修改它

根据以下内容提供简洁的备选解决方案:


注意:上面命令中的预览操作。Remove-WhatIf一旦确定该操作将执行所需操作。

.substring的第二个参数是length,而不是另一个索引。但我可以看到混乱:

'012345'.substring(0,2)
01
但这在指数更高的情况下是行不通的。错误消息引用了长度参数

'01234'.substring(2,3)
234

'012345'.substring(2,5)
Exception calling "Substring" with "2" argument(s): "Index and length must refer to a location within the string.
Parameter name: length"
您可以看到这样的函数定义,没有括号,当给定两个参数时,将长度显示为第二个参数:

'012345'.substring

OverloadDefinitions

string Substring(int startIndex)
string Substring(int startIndex, int length)
所以你可以通过从第二个索引中减去第一个索引得到长度。我也在最后加入了IndexOf-来去掉一个空格

Get-ChildItem *.mp3 |
Rename-Item -NewName {$_.Name.Substring($_.Name.IndexOf("-")+2, $_.Name.IndexOf(".") - ($_.Name.IndexOf("-")+2)) +
  " - " +
  $_.Name.Substring(0, ($_.Name.IndexOf(" -"))) + ".mp3"} -whatif


What if: Performing the operation "Rename File" on target "Item: /Users/js/foo/artist - song.mp3 Destination: /Users/js/foo/song - artist.mp3".
What if: Performing the operation "Rename File" on target "Item: /Users/js/foo/my artist - my song.mp3 Destination: /Users/js/foo/my song - my artist.mp3".
这是另一种方法,在“-”上拆分:


两种解决方案都可以在一条线上。如果看上去不错,就把-whatif脱下来。

回答得很好。将公共参数传递给重命名项将允许直接预览操作。注意:这只是一个诡辩,
因为您的解释仍然适用,但请注意,延迟绑定脚本块(如传递给-NewName的脚本块)不会吞咽异常,它们会报告异常并继续移动到下一个输入对象;只有计算属性中的脚本块才会悄悄地忽略所有错误。OP唯一的问题是对.Substring如何工作的误解。使用带有重命名项-NewName{…}的管道的解决方案不仅更加简洁,而且比循环中的多个重命名项调用性能更好。请允许我向新手提供标准建议:如果答案解决了您的问题,请单击大复选标记接受它✓ 在它旁边,也可以选择向上投票,投票需要至少15个信誉点。如果你发现其他答案有帮助,就投票给他们。接受它,你将获得2点声誉积分,并向上投票帮助未来的读者。有关更多信息,请参阅。如果您的问题尚未完全回答,请提供反馈或建议。
Get-ChildItem *.mp3 |
rename-item -newname { $artist,$song = $_.basename -split ' - ';
                       "$song - $artist.mp3" } -whatif