Help respecting directory structure for file copy

This topic contains 11 replies, has 3 voices, and was last updated by Profile photo of Olaf Soyk Olaf Soyk 2 months, 1 week ago.

  • Author
    Posts
  • #53933
    Profile photo of crashrebootrepeat
    crashrebootrepeat
    Participant

    Hi – This does almost exactly what I Need it to. It goes through a file that I provide and copies the files. However, it does not respect directory structure and will instead just copy all files to the root of the destination.

    Would you please help me solve this so that the files provided in my filename.txt will copy and maintain directory structure.

    function FILEBACKUP($filename) { $filename
    $file = Get-Content "C:\Source\$filename.txt"
    foreach ($line in $file){
    $line2=$line -replace "^([^%]+)?%([^%]+)?%(.*)",'$1$env:$2$3'
    $line2 = $ExecutionContext.InvokeCommand.ExpandString($line2)
    #if ($line -and (test-path ($line2))) {
    Copy-Item $line2 "$WFPROFILE\$filename" 
    #}
    }
    }
    
  • #53958
    Profile photo of Olaf Soyk
    Olaf Soyk
    Participant

    Hi,

    I'm not really sure that I got what you try to do and why you do how you do but some general tips might guide you to a succesfull end.

    I assume you did not provide the complete code – you use a variable $WFPROFILE but in the code you show it's not filled with something.

    As far as I think I got it you're using a text file with structured information to control what your script should do. You get a single line cut it with a regex pattern into pieces, transform it and then use it.
    As Powershell is able to deal really easily with structured text you could use

    Import-CSV

    to get what you need from your text file and feed your loop with it.

    Last but not least: think about formatting your code nicely. Specially when you show it to other people. Code indentation helps understanding more easily. And in a few weeks YOU are other people. 😉

    function FILEBACKUP {
    	param(
    		$Path = "C:\Source\filebackup.txt"
    	)
    	
        $Files = Import-CSV -Path $Path -Delimiter "%" -Header Option1,Option2,Option3
        
        Foreach ($File in $Files) {
            ## do something with the options ... par example:
            ## $Source = $File.Option1 + $File.Option2 
            ## $Destination = $File.Option2 + $ENV:$File.Option3
            Copy-Item -Path $Source -Destination $Destination
        }
    }
    

    ... hope it helps
    PS:> (79,108,97,102|%{[char]$_})-join"

  • #53977
    Profile photo of crashrebootrepeat
    crashrebootrepeat
    Participant

    I apologize for not posting the entire code. However I will post it now. I am not having problems with the rest of the script just the FILEBACKUP part.

    Lets say one of the files I was calling was office 365 – files OK well this script will create a folder based on that file name and then parse through the file and copy the files in the paths of the file IE:

    %APPDATA%\Microsoft\Templates\*.dotm

    The problem is that it does copy the files but it copies them all to the root of the destination. I need to make my FILEBACKUP function respect the entire path. So it should be copying the files to DEST>Microsoft>Templates

    #SET WORK FOLDERS VARIABLE
    $wfprofile = Join-Path "$env:userprofile" -ChildPath "work folders\profile"
    
    #Build Folders
    
    function BuildFolders(){
    $folders = (Get-ChildItem -path C:\Source\).BaseName
    foreach ($item in $folders) {
    $foldername = $item 
    if ($foldername -match "files$") {
    if (!(test-path $wfprofile\$foldername)) {new-item -ItemType Directory -Path $wfprofile -Name $foldername}
    FILEBACKUP($foldername) }
    if ($foldername -match "reg$") {
    if (!(test-path $wfprofile\$foldername)) {new-item -ItemType Directory -Path $wfprofile -Name $foldername}
    #REGBACKUP($foldername) }
    }
    }
    }
    
    function REGBACKUP($filename) { $filename
    $file = Get-Content "C:\Source\$filename.txt"
    foreach ($line in $file){
    if (test-path ($line -replace "^hkcu\\","hkcu:\")) {
    $name = $line -replace "\\","-"
    "attempting $line to $WFPROFILE\$filename\$name.reg" 
    reg Export $line "$WFPROFILE\$filename\$name.reg" /y
    }
    else {write-host $line "does not exist"} 
    }
    }
    
    function FILEBACKUP($filename) { $filename
    $file = Get-Content "C:\Source\$filename.txt"
    foreach ($line in $file){
    $line2=$line -replace "^([^%]+)?%([^%]+)?%(.*)",'$1$env:$2$3'
    $line2 = $ExecutionContext.InvokeCommand.ExpandString($line2)
    #if ($line -and (test-path ($line2))) {
    Copy-Item $line2 "$WFPROFILE\$filename" 
    #}
    }
    }
    
    
    #CALL FUNCTIONS
    
    BuildFolders
    pause
    Clear-Host
    
    • #53980
      Profile photo of Olaf Soyk
      Olaf Soyk
      Participant

      hmmm ... I'm still not sure if I really got how this should work.

      $line2=$line -replace "^([^%]+)?%([^%]+)?%(.*)",'$1$env:$2$3'
      $line2 = $ExecutionContext.InvokeCommand.ExpandString($line2)
      

      these two lines look a little strange for me. What's in these txt files. Could you show some lines from these files?

  • #53978
    Profile photo of Rob Simmers
    Rob Simmers
    Participant
    • #53979
      Profile photo of crashrebootrepeat
      crashrebootrepeat
      Participant

      Unfortunately -container that doesn't work either

  • #53982
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    There are a lot of examples regarding what you are asking. Check out this MCP mag article. I'm just searching for "powershell copy keep folder structure" and there are a lot of examples.

    https://mcpmag.com/articles/2015/10/22/maintain-folder-structure.aspx

    • #54456
      Profile photo of crashrebootrepeat
      crashrebootrepeat
      Participant

      Sorry for the delay in reply, but unfortunately I am still having trouble with this.

      I Need to be able to have this copy the files that I provide from an external file source and respect directory structure.

      An Example of what I am providing to be copied would be like

      %APPDATA%\Microsoft\Office\*.OfficeUI
      %APPDATA%\Microsoft\Access
      %APPDATA%\Microsoft\Excel
      -%APPDATA%\Microsoft\Excel\*.xlsb

      Which is why I Have the code

      $line2=$line -replace "^([^%]+)?%([^%]+)?%(.*)",'$1$env:$2$3'
      $line2 = $ExecutionContext.InvokeCommand.ExpandString($line2)

      So that it knows it can expand the %appdata%

      This script works beautifully minus that it doesn't respect directory structure. I need it to respect directory structure.

      Please someone help me solve this.

    • #54497
      Profile photo of Olaf Soyk
      Olaf Soyk
      Participant

      your copy-item is missing -recurse -force, a leading comma in your 'controler file' will not work and if you already have ...\Microsoft\Excel in your 'controler file' another folder like ...\Microsoft\Excel\*.xlsb does not make sense because it's already included in the other one

      this should work:

      $wfprofile = Join-Path "$env:userprofile" -ChildPath "work folders\profile"
      
      function BuildFolders(){
      $folders = (Get-ChildItem -path C:\Source\).BaseName
          foreach ($folder in $folders) {
              if ($folder -match "files$") {
                  $destination = Join-Path -Path $wfprofile -ChildPath $folder
                  if (-not (test-path -Path $destination)) {
                      new-item -ItemType Directory -Path $destination
                  }
                  FILEBACKUP($folder) 
              }
          }
      }
      
      function FILEBACKUP($filename) { 
          $sourcefolders = Get-Content "C:\Source\$filename.txt"
          foreach ($sourcefolder in $sourcefolders){
              $sourcepath=$sourcefolder -replace "^([^%]+)?%([^%]+)?%(.*)",'$1$env:$2$3'
              $sourcepath = $ExecutionContext.InvokeCommand.ExpandString($sourcepath)
              Copy-Item $sourcepath "$WFPROFILE\$filename" -Recurse -Force
          }
      }
      
      BuildFolders

      But may I suggest another approach for you? If you put to your 'controler file' paths in powershell format your script could be much simpler.

      'controler file':

      $ENV:APPDATA\Microsoft\Office\*.OfficeUI
      $ENV:APPDATA\Microsoft\Access
      $ENV:APPDATA\Microsoft\Excel

      'script':

      $Destination = Join-Path -Path $ENV:USERNAME -ChildPath "work folders\profile"
      $ControlerFiles = Get-ChildItem -Path "C:\Source" -Filter "*files.txt" -File | Select-Object -ExpandProperty Fullname
      foreach ($ControlerFile in $ControlerFiles) {
          $SourcePaths = Get-Content -Path $ControlerFile
          foreach ($SourcePath in $SourcePaths) {
              Copy-Item -Path $SourcePath -Destination $Destination -Recurse -Force
          }
      }
      • This reply was modified 2 months, 1 week ago by Profile photo of Olaf Soyk Olaf Soyk.
    • #54501
      Profile photo of Olaf Soyk
      Olaf Soyk
      Participant

      unfortunately I cannot edit my own post anymore ...
      To resolve the paths given by the 'controler file' you have to put

      $SourcePath = Invoke-Expression  """$SourcePath"""

      before the copy-item cmdlet

    • #54544
      Profile photo of crashrebootrepeat
      crashrebootrepeat
      Participant

      Unfortunately this still does not work.

      1: If I change my copy-item line to include -recurse -force it still does not respect directory structure. Example I am copying the files ending in *.officeUI which live in the office folder. But they don't get copied to the office folder, the go to the root of the destination.

      2: If I try the other way and using the $ENV:APPDATA it completely errors out and says it can't find a drive matching $ENV:APPDATA

    • #54575
      Profile photo of Olaf Soyk
      Olaf Soyk
      Participant

      1: If I change my copy-item line to include -recurse -force it still does not respect directory structure. Example I am copying the files ending in *.officeUI which live in the office folder. But they don't get copied to the office folder, the go to the root of the destination.

      Of course Powershell will not copy the folder if you specify the files to be copied. 😉

      Maybe you have to change your controler file to provide the folder you are looking for AND the file pattern:
      'controler file':

      path,filepattern
      $ENV:APPDATA\Microsoft\Office,*.officeUI
      $ENV:APPDATA\Microsoft\Access,*
      $ENV:APPDATA\Microsoft\Excel,*

      So you have to use import-csv to get the information about what you want to backup:
      'backup script'

      $Destination = Join-Path -Path $ENV:USERPROFILE -ChildPath "work folders\profile"
      $ControlerFiles = Get-ChildItem -Path "C:\Source" -Filter "*files.txt" -File | Select-Object -ExpandProperty Fullname
      foreach ($ControlerFile in $ControlerFiles) {
          $Sources = Import-Csv -Delimiter "," -Path $ControlerFile
          foreach ($Source in $Sources) {
              $SourcePath = Invoke-Expression  """$($Source.path)"""
              $FilePattern = $Source.filepattern
              Copy-Item -Path $SourcePath -Filter $FilePattern -Destination $Destination -Recurse -Force
          }
      }

      That should catch the folder with the files you're looking for.

You must be logged in to reply to this topic.