Replicate Directory Structure in new location

I am looking to try and replicate a File Structure on a on-going basis.
Currently I have written this, but my results come back a bit funky.

$j12912 = ‘’
$sfts = ‘.sfts’
$client = ‘’

$table = get-childitem $j12912 -Directory -recurse | where { $_.FullName -notmatch ‘.stfs’ } | select-object Name

foreach($folder in $table){
write-host $client$folder
}

However my results are

@{Name=Data Requests}
When I am looking for
Data Requests

I am not sure how to remove that @{Name= }

Once I figure that out, I can that add a MkDir into the Foreach loop if the Directory does not exist.
Any Help would be great,
Thank you,

Sean Edler

Sean,
Welcome to the forum. :wave:t4:

When you post code or sample data or console output please format it as code using the preformatted text button ( </> ). Simply place your cursor on an empty line, click the button and paste your code.
Thanks in advance

You may take a look at robocopy. If it’s about copying or moving files and folders in most cases there’s no need for a script. :wink:

Regardless of that: What do you want to do with your code? At the moment you’re only listing folders. And for that you don’t need a loop and you don’t need - no, actually you should not use - Write-Host. :wink:

Hey Olaf,
Thank you for replying to me.
Essentially the end goal is that Clients will drop off data files through an SFTP site but where the files get dropped determine where they get automatically routed to with (this powershell script).
I could setup static code that just moves from this folder to this other folder, but I want the code to have the ability to detect if a new folder has been created, and if it has to create this folder in the location the files get moved to.
The Clients SFTP drops them into a home folder. in this Folder they see Job1, Job2, Job3
Those Jobs have data stored internally for different teams, so the files get routed to different places once they are dropped into the proper Job folder.
Sometimes the Job folder also has sub folders.
The folder structure will mimic in the Internal file directory.

Lets say the Client SFTP looks like this
//Source/job1/files/Jan
//Source/job1/files/Feb
//Source/job1/files/Mar
//Source/job1/files/Apr

Internally let’s say we have this
/Dest/job1/files/Jan
//Dest/job1/files/Feb
//Dest/job1/files/Mar

I want to detect that the Apr folder has been created, and that it does not exist in the destination, so it will create it before moving to the next step in the process.
After it creates it, it then checks for files inside the folders to see if they need to be moved to the internal locations.

$j12912 = '<FilePath1>'
$sfts = '.sfts'
$client = '<ClientFilePath>'
$table = get-childitem $j12912 -Directory -recurse | where { $_.FullName -notmatch '.stfs' } | select-object Name

foreach($folder in $table){
   write-host $client$folder
}

However these are my results

<ClientFilePath>@{Name=Data Requests}
<ClientFilePath>@{Name=old}
<ClientFilePath>@{Name=Previous Daily Files}
<ClientFilePath>@{Name=Rebuttals}

I was looking for

<ClientFilePath>Data Requests
<ClientFilePath>old
<ClientFilePath>Previous Daily Files
<ClientFilePath>Rebuttals

Hmmm … I’m unsure if PowerShell is the right tool for this job. First of all PowerShell is not able to handle ftp server connections out of the box.

Anyway - as I already stated in my first answer - you should not use Write-Host.

$j12912 = '<FilePath1>'
$sfts = '.sfts'
$client = '<ClientFilePath>'
$table = 
    Get-ChildItem -Path $j12912 -Directory -Recurse | 
        Where-Object -Property FullName -notmatch -Value $sfts

foreach ($folder in $table) {
    "$client$($folder.Name)"
}

Regardless of that - when you use a comparison operator like -notmatch what is working with regular expressions you have to take care of special characters like the period. In regex a period means “any character”!! :point_up_2:t4:

1 Like

Thank you so much, that change you suggested was perfect for what I needed.

Great. :+1:t4:

BTW: There is a PowerShell way of building a proper path:

$Dest = '\\Server\transfers\phase2\Jobs\J12192\File Submission'
$sfts = @('.sfts', 'attrs', 'links', 'refs')
$client = '\\Server\transfers\cfis\Client01\J12192\File Submission\'

function Check-Folders {
$table = 
    Get-ChildItem -Path $Dest -Directory -Recurse | 
        Where-Object -Property FullName -notmatch -Value $sfts

foreach($folder in $table){

   $Path = "$client$($folder.Name)"
   IF(!(test-path $Path))
   {
   Mkdir "$client$($folder.Name)"
   }
   IF(test-path $Path)
   {
   Get-ChildItem -Path $client$($folder.Name) -Recurse -File | Move-Item -Destination "$Dest"\"$($folder.Name)"
   Write-Host "-Path "$client$($folder.Name)""\*.*" "$Dest"\"$($folder.Name)"\"""
   }

   Remove-folders
   

}}

function remove-folders {
IF(test-path $client'.stfs')
   {
   remove-item $client'.stfs'
   }
   IF(test-path $client'attrs')
   {
   remove-item $client'attrs'
   }
   IF(test-path $client'refs')
   {
   remove-item $client'refs'
   }
   IF(test-path $client'links')
   {
   remove-item $client'links'
   }
}

function Move-Files {
Get-ChildItem -Path $client -Recurse -File | Move-Item -Destination $Dest


}

Check-Folders
#Move-Files

 

This throws an error, but I feel I have gotten very close now. 

The Only reason I do Write-Host is to see my output, it’s not something I leave in.

right now any folder with a file throws the same error

Move-Item : The input object cannot be bound to any parameters for the command either because the command does not take 
pipeline input or the input and its properties do not match any of the parameters that take pipeline input.
At line:19 char:64
+ ... me) -Recurse -File | Move-Item -Destination "$Dest"\"$($folder.Name)"
+                          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (Test01.txt:PSObject) [Move-Item], ParameterBindingException
    + FullyQualifiedErrorId : InputObjectNotBound,Microsoft.PowerShell.Commands.MoveItemCommand

I had quotes in the wrong spot. Nvm, thanks again for all your help.

Sean,

sounds like you’ve got your code working. Let me try to give you some hopefully useful tips to improve your code anyway.

  • The verb Check for a PowerShell function is not a good idea because Check is not an approved verb and your function does more than just checking something. :wink:
  • Using variables from another scope inside a function is considered bad style. Instead you should provide everything what’s needed for the function as parameter to the function
  • It’s considered bad style to use aliasses in scripts.
  • Instead of Write-Host you should use Write-Debug or Write-Verbose. When you use the attribute [CmdletBinding] for your functions you can use the common parameter -Verbose or -Debug occationally when needed for debugging or to see verbose output. about_Functions_CmdletBindingAttribute
  • Avoid writing repetitive code. When you write a code line more than twice you should consider using a loop or a function.

You may read more about good coding style and best practices in the

If I got it right something like this could be a starting point for you:

function Sync-Folder {
    [CmdletBinding()]
    param (
        [Parameter(Mandatory = $true)]
        [string]
        $Source,
        [Parameter(Mandatory = $true)]
        [string]
        $Destination,
        [string[]]
        $ExcludeFolderList
    )
    begin {
        $FolderList = 
            Get-ChildItem -Path $Destination -Directory -Recurse -Exclude $ExcludeFolderList
    }

    process {
        foreach ($folder in $FolderList) {

            $Path = Join-Path -Path $Source -ChildPath $($folder.Name)
            IF ( -not (test-path $Path)) {
                New-Item -Path $Path -ItemType Directory | Out-Null
            }
            $DestinationFolder = Join-Path -Path $Destination -ChildPath $($folder.Name)
            Write-Verbose "Moving files from '$(Path)' to '$($DestinationFolder)'"
            Get-ChildItem -Path $Path -Recurse -File | Move-Item -Destination $DestinationFolder
        }
    }

    end {
        if ($ExcludeFolderList) {
            foreach ($ExcludeFolder in $ExcludeFolderList) {
                $Path = Join-Path -Path $Source -ChildPath $ExcludeFolder
                if (Test-Path -Path $Path) {
                    Remove-Item -Path $Path -Recurse -Force
                }
            }
        }
    }
}

Now you don’t depend on variables from an outside scope anymore. To run this function you should use splatting to avoid having super long code lines and make your code easier to read:

$SyncFolderParams = @{
    Source            = '\\Server\transfers\cfis\Client01\J12192\File Submission\'
    Destination       = '\\Server\transfers\phase2\Jobs\J12192\File Submission'
    ExcludeFolderList = 'sfts', 'attrs', 'links', 'refs'
}
Sync-Folder $SyncFolderParams -Verbose

Test it and when you’re satisfied you may remove the parameter -WhatIf from the potentially harmful cmdlets to make the code actually working. :wink:

1 Like