如何在Powershell Pipeline(子字符串方法)中多次使用$ _

我正在尝试编写一种自动化程序,以便可以按如下所述提供输入并获得指示的输出,而我正在使用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作为更大解决方案的一部分来实现此目的。

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

Get-ChildItem *.mp3 | Rename-Item -NewName {$_.Name.Substring($_.Name.IndexOf("-")+2,$_.Name.IndexOf(".")) +" - " + $_.Name.Substring(0,($_.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。

如何获取以下逻辑的输出(我在此处执行Select只是为了进行故障排除,因为在故障排除时我不想实际重命名)

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

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

yzmjsk92856 回答:如何在Powershell Pipeline(子字符串方法)中多次使用$ _

问题的根源是您的Substring表达式。如果您暂时将其从管道中断开,您会发现有一个错误正在被吞噬...

PS> $s = "my artist - my song.mp3"
PS> $s.Substring($s.IndexOf("-") + 2,$s.IndexOf("."))
Exception calling "Substring" with "2" argument(s): "Index and length must refer to a location within the string.
Parameter name: length"
At line:1 char:1
+ $s.Substring($s.IndexOf("-") + 2,$s.IndexOf("."))
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [],ParentContainsErrorRecordException
    + FullyQualifiedErrorId : ArgumentOutOfRangeException

问题是Substring带有一个startIndexlength参数,但是您试图给它一个startIndexendIndex。结果是由于管道中引发了异常,因此您的新名称是一个空字符串。

您可以使用正则表达式来解决此问题-类似于:

PS> $s = "my artist - my song.mp3"
PS> $matches = [regex]::Match($s,"(?<artist>.*) - (?<song>.*).mp3")
PS> $newname = $matches.Groups["song"].Value + " - " + $matches.Groups["artist"].Value + ".mp3"
PS> $newname
my song - my artist.mp3

如果将其放回管道中,则会得到:

Get-ChildItem *.mp3 | Rename-Item -NewName {
    $matches = [regex]::Match($_.Name,"(?<artist>.*) - (?<song>.*).mp3")
    $matches.Groups["song"].Value + " - " + $matches.Groups["artist"].Value + ".mp3"
}

但是在您承诺重命名整个音乐库之前,请与Select-Object一起玩:-)

,

让我 complement mclayton's helpful answer,它很好地解释了您的尝试问题,并提供了一个优雅的基于正则表达式的解决方案。

关于这个问题和声明的标题:

  

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

在脚本块中可以使用$_ 的次数没有,如下例所示:

PS> 'one' | ForEach-Object { $_,$_.Substring(1),$_.SubString(2) -join '-' }
one-ne-e

从技术上讲,可以进行 modify 的修改,因此也可以丢弃$_的值,但是应该避免使用-与对 all { {3}}。

诸如.Substring()之类的字符串方法从根本上返回输入字符串的(已修改)副本-他们从不对它们进行修改


要基于automatic variables提供一种简洁的替代解决方案:

Get-ChildItem *.mp3 | Rename-Item -NewName {
  ($_.BaseName -replace '^(.+?) - (.+)','$2 - $1') + $_.Extension
} -WhatIf

注意:上方命令中的-replace operator会预览该操作。确定操作将执行您想要的操作后,请删除-WhatIf

,

以您尝试的方式重命名这些文件时,我也一筹莫展。

我能够用更多的代码来完成这项工作。 (如果您确实愿意,可以将其卡成一行):

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}

您显然可以从此代码中删除Write-Host,因为我使用它们来检查名称结构以确保它是我想要的样子。

此代码假定将使用以下格式命名所有文件: “艺术家姓名-歌曲Name.mp3”

示例:

Korn - Blind.mp3

Rob Zombie - Dragula.mp3

Luinz - I got 5 on it.mp3

希望这会有所帮助

,

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

'012345'.substring(0,2)
01

但是,对于更高的索引,这将不成立。错误消息引用了length参数。

'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".

这是另一种方法,分为' - '

Get-ChildItem *.mp3 |
rename-item -newname { $artist,$song = $_.basename -split ' - ';
                       "$song - $artist.mp3" } -whatif

任何一种解决方案都可以在同一行上。如果看起来不错,请脱掉-whatif。

本文链接:https://www.f2er.com/3130374.html

大家都在问