1

I am writing a PS script that takes a flattened directory structure, finds the pdfs, and copies them to a renamed folder with a sequential name, ie, 1.pdf, 2.pdf, etc. What I want to do is make this accommodate nested directories in the Location, but I am a bit of a novice with PS and don't know how to do this while maintaining the count variable.

Here is what I have:

Set-Location -path "C:\Users\mmcintyre\Desktop\test pdfs"
Get-ChildItem *.pdf | 
ForEach-Object  -Begin { $count = 1 } -Process { 
    Copy-Item -LiteralPath $_ -Destination ".\renamed\$count.pdf"; 
    Write-Host "Renamed $($_.fullname) to $count.pdf"
    $count++ 
}

Any help would be greatly appreciated.

Edit: As I was on Powershell =< 2.0, I needed to use the following in addition to the Recurse:

Get-ChildItem -Path .\* -Include *.pdf -Recurse |

which works now. Hope this helps someone else.

Michael McIntyre
  • 195
  • 1
  • 12

1 Answers1

0

Here's a simplified PSv2+ solution:

$countRef = [ref] 0
Get-ChildItem -Filter *.pdf -Recurse |
  Copy-Item -WhatIf -Destination { '.\renamed\{0}.pdf' -f ++$countRef.Value }

-WhatIf previews the copy operations; remove it to perform actual copying.

  • As in your solution, -Recurse is needed to traverse the entire directory subtree.

  • -Filter is used instead of -Include, because it is:

    • noticeably faster
    • and doesn't require the workaround of passing .\* as the input path (because -Include patterns only apply to the last path component as specified)
    • An explanation can be found in this answer of mine.
    • Also note that in PSv3+ you could also use Get-ChildItem *.pdf -Recurse, in which case *.pdf implicitly binds to the -Path parameter; however, as you've noticed, that doesn't work in PSv2 (but using -Filter does).
  • A script-block parameter is used to specify the destination path, which obviates the need for a slower ForEach-Object call.

    • Note the need for a [ref] instance as the sequence-number value holder, and the need to access the value via .Count.
      • This is necessary, because the script block passed to -Destination runs in a child variable scope rather than in the caller's scope (the way script blocks passed to ForEach-Object do); this discrepancy is discussed in this GitHub issue.
mklement0
  • 382,024
  • 64
  • 607
  • 775