When did changing filenames become so complicated?? All I want to do is add '_a' to a directory of files (with no extension)
The following does not work:
Rename-Item -newname { $_BaseName + '_a' } -whatif
Rename-Item : Cannot evaluate parameter 'NewName' because its argument is specified as a script block and there is no
input. A script block cannot be evaluated without input.
At line:1 char:22
+ Rename-Item -newname { $_BaseName + '_a' } -whatif
+ ~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : MetadataError: (:) [Rename-Item], ParameterBindingException
+ FullyQualifiedErrorId : ScriptBlockArgumentNoInput,Microsoft.PowerShell.Commands.RenameItemCommand
then:
Rename-Item -Path C:\users\r-waibel\Downloads\Mar723_pod3-m $_.basename -Newname ($_.basename + "_a") -whatif
Rename-Item : A positional parameter cannot be found that accepts argument '$null'.
At line:1 char:1
+ rename-item -Path C:\users\r-waibel\Downloads\Mar723_pod3-m $_.basena ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo : InvalidArgument: (:) [Rename-Item], ParameterBindingException
+ FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.RenameItemCommand
Let me add some background information to Mathias R. Jessen's effective solution:
The automatic $_
variable only ever has a meaningful value inside script blocks ({ ... }
), and most commonly refers to the current input object received via the pipeline.
The script block passed to -NewName
in your
Rename-Item -NewName { $_BaseName + '_a' } -WhatIf
command is a so-called delay-bind script block, which therefore indeed requires pipeline input:
Note that delay-bind script blocks only work with parameters that are explicitly designed to bind to pipeline input (and that they mustn't be of type [script block]
or [object]
).
In effect, instead of providing a static value to -NewName
you're passing a piece of code that gets evaluated for each pipeline input object and therefore allows the calculation of dynamic, per-input-object values.
If there is no pipeline input, the command cannot function, and you'll get the error that includes Cannot evaluate parameter 'NewName' because its argument is specified as a script block and there is no input.
By contrast, -NewName ($_.basename + "_a")
passes an (ultimately) static value to -NewName
; that is, the expression is evaluated once, up front, and its result is passed as a static value to the parameter.
For the reasons explained above, $_
has no meaningful value in this context; it is $null
, so $_.basename
is $null
too, and the overall result is just _a
.
However, because you also tried to use $_.basename
as an additional, positional argument
(rename-item -Path C:\users\r-waibel\Downloads\Mar723_pod3-m $_.basename -Newname ($_.basename + "_a") -whatif
), the Rename-Item
call broke fundamentally, due to that argument (whose value happens to be $null
) not being recognized.
This is what the error message A positional parameter cannot be found that accepts argument '$null'.
indicates.
To solve your task without a delay-bind script block (the latter being preferable), but without having to hard-code the input file name, you'd need an aux. custom variable; e.g.:
$file = Get-Item -LiteralPath C:\users\r-waibel\Downloads\Mar723_pod3-m
$file | Rename-Item -NewName ($file.BaseName + '_a') -WhatIf
The above is the - more cumbersome, single-file-only - equivalent to using a delay-bind script block as follows:
Get-Item -LiteralPath C:\users\r-waibel\Downloads\Mar723_pod3-m |
Rename-Item -NewName { $_.BaseName + '_a' } -WhatIf
As for:
All I want to do is add '_a' to a directory of files (with no extension)
Using the flexibility of delay-bind script blocks discussed above, this is then as simple as piping the output from a Get-ChildItem
call that outputs all files to rename to your Rename-Item
call:
Get-ChildItem -File -LiteralPath C:\users\r-waibel\Downloads\ |
Rename-Item -NewName { $_.BaseName + '_a' } -WhatIf