Как использовать $ _ более одного раза в Powershell Pipeline (метод подстроки) - PullRequest
2 голосов
/ 10 ноября 2019

Я пытаюсь написать автоматизацию таким образом, чтобы я мог дать ввод, как описано ниже, и получить указанный вывод, и я использую 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 для получения элементов, а затем их переименования. Поэтому я попробовал это:

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 выдает результат для каждого прогона. Однако, если я запускаю оба командлета вместе, выводит только первая строка, вторая возвращает NULL.

Как получить вывод для приведенной ниже логики (я делаю выбор здесь только для устранения неполадок, как и яне хочу на самом деле переименовывать при устранении неполадок)

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

Я могу использовать переменные и соединять строки, но стараюсь держать процесс в одной строке, если это возможно, без написания пользовательского кода / модуля.

Ответы [ 4 ]

3 голосов
/ 10 ноября 2019

Корень вашей проблемы - ваше 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

Проблема в том, что Подстрока занимает startIndexи length параметр, но вы пытаетесь присвоить ему startIndex и endIndex. В результате ваше новое имя будет пустой строкой из-за исключения, генерируемого внутри конвейера.

Вместо этого вы можете исправить это с помощью регулярного выражения - что-то вроде:

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, прежде чем вы решите переименовать всю свою музыкальную библиотеку: -)

1 голос
/ 10 ноября 2019

Позвольте мне дополнить полезный ответ Маклайтона , который хорошо объясняет проблему с вашей попыткой и предлагает элегантное решение на основе регулярных выражений.

Что касаетсязаголовок этого вопроса и этого утверждения:

Второй IndexOf в разделе подстроки получает нулевое значение, поскольку он используется дважды в одной и той же (подстроке) операции дважды.

Существует нет ограничения на то, сколько раз вы можете использовать $_ в блоке сценария, как показано в следующем примере:

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

Этотехнически возможно изменить и, следовательно, также отбросить значение $_, но этого следует избегать - как и должно быть изменение всех автоматических переменных .

Строковые методы, такие как .Substring(), по существу возвращают (измененную) копию входной строки - они никогда не изменяют ее вместо .


Чтобы предложить краткое, альтернативное решениеn на основе оператора -replace :

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

Примечание: -WhatIf общий параметр в приведенной выше команде previews операция. Удалите -WhatIf, как только вы убедитесь, что операция будет выполнять то, что вы хотите.

0 голосов
/ 10 ноября 2019
Второй аргумент

.substring () - длина , а не другой индекс. Но я мог видеть путаницу:

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

Вот еще один подход, разбивающий на ' - ':

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

Любое решение может бытьна одной линии. Снимите -whatif, если он выглядит хорошо.

0 голосов
/ 10 ноября 2019

Я также был бесплоден, когда дело доходит до переименования этих файлов способом, который вы пытаетесь.

Я смог заставить это работать с немного большим количеством кода. (Вы можете вставить его в одну строку, если вы действительно этого хотите):

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 из этого кода, так как я использовал их, чтобы проверить структуру имени, чтобы убедиться, что все было так, как я хотел.

В этом коде предполагается, что ВСЕ файлы будут названы в следующем формате: «Имя исполнителя - Song Name.mp3»

Примеры:

Korn - Blind.mp3

Rob Zombie - Dragula.mp3

Luinz - I got 5 on it.mp3

Надеюсь, это поможет

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...