Reputation: 67
I am attempting to batch edit metadata of some video files by piping the results from ls into a function that edits the video file's name in metadata. Example:
Files:
Desired title renames in metadata:
I am using the mkvpropedit function from MKVToolNix in PowerShell, which can be used to rename a single file's title in the following way:
C:\"Program Files"\MKVToolNix\mkvpropedit 'My Movie 1 - 1080p DTS 5.1.mkv' -e info -s title='My Movie 1'
This works perfectly, however I would like to be able to perform this action for an entire directory, removing everything from the dash on in the metadata title. Here is the code I tried below, piping the results of ls into the command (I should mention that every single file in the directory is a movie file I wish to edit) and capturing the desired portion of the filename with a regex.
ls | C:\"Program Files"\MKVToolNix\mkvpropedit {$_.name} -e info -s title={$_.name -replace('(.+)-.+', '$1')}
The regex is correct as far as I can tell, but I receive the following error:
mkvpropedit.exe : The command parameter was already specified. At line:1 char:6
ls | C:"Program Files"\MKVToolNix\mkvpropedit {$_.name} -e info -s t ... ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CategoryInfo : InvalidArgument: (:) [], ParameterBindingException
FullyQualifiedErrorId : ParameterSpecifiedAlready
As suggested in the comments, I tried a foreach loop as well:
ls | foreach{C:\"Program Files"\MKVToolNix\mkvpropedit {$_.name} -e info -s title={$_.name -replace('(.+)-.+', '$1')}}
This produced the same error, once for each file in the directory. What am I doing wrong here? Is there a better way to approach my desired outcome? Thanks!
Upvotes: 3
Views: 2059
Reputation: 439757
It looks like you're trying to use delay-bind script blocks ({ ... }
blocks that reference $_
as the current pipeline-input variable), which, however, are not supported in direct calls to external executables such as mkvpropedit
.
Instead, you must use a ForEach-Object
call and use expressions based on $_
to formulate your arguments:
Get-ChildItem | ForEach-Object {
C:\"Program Files"\MKVToolNix\mkvpropedit $_.name -e info -s title=$($_.name -replace '(.+)-.+', '$1')
}
$_.name
passed the current pipeline-input object's ($_
's) .name
property as an argument.
title=$($_.name -replace '(.+)-.+', '$1')
uses $()
, the subexpression operator, to incorporate the result of an expression into the single title=<value>
argument.
()
, the grouping operator by itself is sufficient to pass the result of an expression as an argument, in the case of a compound argument such as title=<value>
it does not work, because PowerShell's parameter binder considers the (...)
expression a separate argument.$()
instead does work as a compound token, because PowerShell then treats the entire token implicitly as if it were enclosed in "..."
, i.e. it treats it as an expandable string.Note: While invocation via C:\"Program Files"\MKVToolNix\mkvpropedit
does work, the quoting style is unusual; more typically, you'd enclose the whole path in quotes:
"C:\Program Files\MKVToolNix\mkvpropedit"
(or, given that there are no variables to expand, with single quote:
'C:\Program Files\MKVToolNix\mkvpropedit'
However, if the path is quoted as a whole and/or contains a variable reference, you must invoke via &
, the call operator, for syntactical reasons:
& 'C:\Program Files\MKVToolNix\mkvpropedit' ...
Upvotes: 4