I created this very simple PowerShell script:
using namespace System.IO
[CmdletBinding(SupportsShouldProcess)]
param
(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)][FileInfo[]]$Files
)
$Files.Length
$Files | Sort-Object | ForEach-Object {$_.Name}
When I call it with any result of a Get-ChildItem
call, $Files.Length
is always 1
, no matter how many files are in a directory:
PS C:\Temp> Get-ChildItem -File C:\Windows\ | .\Rename-Test.ps1
1
WMSysPr9.prx
What did I do wrong?
Basically your script is only missing a process
block otherwise, the default block is end
thus you would be only seeing the last element coming from the pipeline. A minimal example of how you can approach it:
using namespace System.IO
[CmdletBinding(SupportsShouldProcess, ConfirmImpact = 'High')]
param
(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[FileInfo[]] $Files
)
process {
foreach($file in $Files) {
if($PSCmdlet.ShouldProcess($file.Name, 'Doing something')) {
$file.FullName
}
}
}
As you may note, since the -Files
parameter is typed [FileInfo[]]
(array of FileInfo
instances), a loop is required in case the argument is passed via named parameter or positionally.
If however you need to collect all input, i.e. for sorting it, then you would be needing a List<T>
to collect each object in the process
block and then work with the collected input in the end
block, for example:
using namespace System.IO
[CmdletBinding()]
param
(
[Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)]
[FileInfo[]] $Files
)
begin {
$items = [System.Collections.Generic.List[FileInfo]]::new()
}
process {
$items.AddRange($Files)
}
end {
$items | Sort-Object Length
}
Then for the above, both ways would work fine:
# from pipeline
Get-ChildItem -File | .\myscript.ps1
# from positional binding
.\myscript.ps1 (Get-ChildItem -File)