1

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.

6
  • what is exactly the problem? What is not working as you expect it? Commented Dec 17, 2021 at 8:04
  • 2
    Remove FullName and $_ - Copy-Item implicitly binds the pipeline input ($_ is only needed in script blocks). Commented Dec 17, 2021 at 8:06
  • If I remove the last part beginning with "| Copy-Item", I get the last file name that I am interested in. However the Copy-Item item command does not work. Commented Dec 17, 2021 at 8:09
  • 1
    @mklement0 - ok, the script works as expected when I remove FullName and $_ . Didn't know you can specify Copy-Item without source file name. Thank you ! Commented Dec 17, 2021 at 8:17
  • 1
    @ggv but you are specifying the source file name - you're just doing so via the pipeline :) Commented Dec 17, 2021 at 11:30

1 Answer 1

1

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.
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.