Hayco
Hayco

Reputation: 37

Powershell: how to loop through folders and execute the same code for each of the folders

Currently I have a script that will sort files in a folder (on their lastwritetime), keep the latest file and move the other files to a different folder. This works correctly:

Get-ChildItem "\\networkfolder\RawData\2_ActionData_Prep\CustomerA\" -Recurse -Filter "*.rpt" -File |               
    Sort-Object -Property LastWriteTime -Descending | 
    Select-Object -Skip 1 |                                                          
    Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"

The problem is that I have several 'customer' folders and I want to execute the code above in each of those folders. I tried the following:

$CustomerFolders = Get-ChildItem -Path "\\networkfolder\RawData\2_ActionData_Prep\" -Directory -Recurse

foreach ($folder in $CustomerFolders) {Get-ChildItem -Filter "*.rpt" -File | Sort-Object -Property LastWriteTime -Descending  | 
Select-Object -Skip 1 |                                                          
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"}  

When I execute this script, nothing happens. Also no error comes up. Hopefully someone could help me on this.

Santiago Squarzon noticed that a $folder was missing, so I added $folder in loop for Get-Childitem:

$CustomerFolders = Get-ChildItem -Path "\\networkfolder\RawData\2_ActionData_Prep\" -Directory -Recurse

foreach ($folder in $CustomerFolders) {Get-ChildItem $folder -Filter "*.rpt" -File | Sort-Object -Property LastWriteTime -Descending  | 
Select-Object -Skip 1 |                                                          
Move-Item -Force -Destination "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"}  

Now I get an error message:

Get-ChildItem : Cannot find path '\networkfolder\CustomerA' because it does not exist.

It somehow misses the part \RawData\2_ActionData_Prep\ in the path, although I defined it in the $CustomerFolders variable?

Upvotes: 1

Views: 799

Answers (1)

Santiago Squarzon
Santiago Squarzon

Reputation: 60060

You could do the process all with pipelines like this:

$base = "\\networkfolder\RawData\2_ActionData_Prep\"
$destination = "\\networkfolder\RawData\_Archive\Archive_DataRetrieved\"

Get-ChildItem -Path $base -Directory -Recurse | ForEach-Object {
    $_ | Get-ChildItem -Filter "*.rpt" -File | Sort-Object LastWriteTime -Descending |
        Select-Object -Skip 1 | Move-Item -Force -Destination $destination
}

To briefly explain why Get-ChildItem $folder... failed but $folder | Get-ChildItem ... worked, when we do Get-ChildItem $folder, $folder is being passed as argument for the -Path parameter and the parameter type for it is [string[]]. So, in your code when $folder (a DirectoryInfo instance) is passed as argument, it is being converted to a string and, very unfortunately in Windows PowerShell, when we type convert a DirectoryInfo (and a FileInfo too!) object to string what we get as a result is the Directory Name (this is not the case in PowerShell Core, where the string representation of this object becomes the Directory FullName (a.k.a. Absolute Path) so Get-ChildItem thinks it's being fed a relative path and it looking for the folders in your current location.

However when we do $folder | Get-ChildItem ..., $folder gets bound to the -LiteralPath parameter by Property Name on the PSPath ETS property, in other words, the cmdlet receives the object's provider path (you can think of it as the absolute path of the folder) hence why it works fine.

Upvotes: 1

Related Questions