ggv
ggv

Reputation: 109

PowerShell script that accepts pipeline input?

Need a little help to tweak this script that copies the latest file to another folder:

$FilePath        = "C:\Downloads\Sales 202112*.xlsx"
$DestinationPath = "C:\myFiles\"

gci -Path $FilePath -File | 
  Sort-Object -Property LastWriteTime -Descending | 
    Select FullName -First 1 |
      Copy-Item $_ -Destination $DestinationPath 

Not sure how to reference pipeline input for the Copy-Item command.

Thanks.

Upvotes: 1

Views: 403

Answers (1)

mklement0
mklement0

Reputation: 437080

tl;dr

Get-ChildItem -Path $FilePath -File |
  Sort-Object -Property LastWriteTime -Descending | 
    Select-Object -First 1 | # Note: No 'FullName'
      Copy-Item -Destination $DestinationPath # Note: No '$_'

The simplest and most robust approach is to pipe Get-ChildItem / Get-Item output as-is to other file-processing cmdlets, which binds to the latter's -LiteralPath parameter, i.e the input file path.


As for what you tried:

  • The automatic $_ variable, which explicitly refers to the pipeline input object at hand, is only needed (and supported) inside script blocks ({ ... }) passed to cmdlets.

  • With suitable pipeline input, PowerShell implicitly binds it to a pipeline-binding parameter of the target cmdlet, which in case of the Copy-Item call above is -LiteralPath. In other words: specifying a value for the target parameter as an argument isn't necessary.

    • This answer explains the mechanism that binds the System.IO.FileInfo and System.IO.DirectoryInfo instances that Get-ChildItem outputs to the -LiteralPath parameter of file-processing cmdlets such as Copy-Item.

    • Note that, with FullName out of the picture (see below), it is indeed a System.IO.FileInfo instance that Copy-Item receives as pipeline input, because the Sort-Object ... and Select-Object -First 1 calls above pass them through without changing their type.

  • Selecting the FullName property in an attempt to pass only the file's full path (as a string) via the pipeline is unnecessary, and, more importantly:

    • It fails, unless you use -ExpandProperty FullName to ensure that only the property value is output; without it, you get an object that has a .FullName property - see this answer for more information.
    • Even if you fix that, the solution is - at least hypothetically - less robust than passing the System.IO.FileInfo instances as a whole: mere string input binds to the -Path rather than the -LiteralPath parameter, which means that what is by definition a literal path is interpreted as a wildcard expression, which notably causes mishandling of literal paths that contain [ characters (e.g. c:\foo\file[1].txt).
    • See this answer for how to inspect the specifics of the pipeline-binding parameters of any given cmdlet.

Upvotes: 1

Related Questions