help create function to backup a fileset

This topic contains 4 replies, has 3 voices, and was last updated by Profile photo of Tony Pagliaro Tony Pagliaro 1 year, 8 months ago.

  • Author
    Posts
  • #27140
    Profile photo of Tony Pagliaro
    Tony Pagliaro
    Participant

    I work with a lot of time-stamped CSV files generated automatically by a system we have. I'd like to write scripts to manipulate these files to compare system status from one moment to the next, but first I want to copy them as a backup in case I want to revert changes.

    I've decided to create a Backup-File function in my $profile to call in all my scripts, I hope that's the right move. I would like to create a function that works for any filetype, not just CSVs. In theory it would take all the files in the working directory and copy them into a subfolder therein, adding a " (Copy #)" to the end of the filename ...before the extension... to preserve all copies. I spent all damn day working on this and I've come up with a nice chunk of code that just doesn't seem to work.

    I can select parts of it and walk through the script in parts, without getting an error, and I see the files being created properly. But when I run the whole thing, it doesn't do anything!

    I've even gone so far as to start putting in Write-Debug lines but those don't work either unless I select those smaller blocks individually.

    To test this script, create a temp folder with some files of any type, run the script in that directory, and see if the files you created were copied to a new subfolder under your temp folder. Run it again, to see if a 2nd set is created with different names.

    function Backup-File ([string]$BkpFolder = "Backup", [string]$FileType = "*")
    {
    $DebugPreference = "Continue" 
    
        # Create target path if it doesn't exist
        {
            if (-not (Test-Path $BkpFolder))
            {
                New-Item -Path $BkpFolder -ItemType directory |out-null 
            }
        } # END ... path if it doesn't exist
    
        # Master object to variable
        {
            $FileObjects = Get-ChildItem ("*." + [string]$FileType) | Sort-Object Length -Descending
            
        } # END ... to variable
    
        # Introduce variables for backup file copy operation, so the destination can be different if needed. 
        {
            
            [int]$i = $FileObjects.Count - 1 
            While ($i -gt -1) {
                Write-Debug 'While $i > -1 ... Setting 3 variables'
                Set-Variable -Name ("SourceFileObject"      + [string]$i) -Value $FileObjects[$i]
                Set-Variable -Name ("DestinationFilename" + [string]$i) -Value $FileObjects[$i].Name
                Set-Variable -Name ("DestinationFilePath" + [string]$i) -Value (".\" + ($BkpFolder) + "\" + ($FileObjects[$i]).Name)
    
                # Incrementally rename the file instead of overwriting any existing. 
                {
                    # Check to see if the dest filename matches the path filename of the source
                    If (Test-Path -Path ((Get-Variable -Name ("DestinationFilePath" + [string]$i)).Value)) 
                    {
                    Write-Debug 'If $DestinationFilename# exists in target ... find a new name'
                        # Iterate until you have a unique filename
                        $j = 2
                        While (Test-Path -Path ((Get-Variable -Name ("DestinationFilePath" + [string]$i)).Value)) 
                        {
                             Write-Debug 'While $DestinationFilename# exists in target ... change name'
                             $copyJ = (" (Copy " + [string]$j + ")")
                             Set-Variable -Name ("DestinationFilePath" + [string]$i) `
                                          -Value (".\" + ($BkpFolder) + "\" `
                                                   + ($FileObjects[$i].Name).TrimEnd($FileObjects[$i].Extension) `
                                                   + $copyJ + $FileObjects[$i].Extension)
                        $j += 1
                        } 
                    }
                Write-Debug 'Copying file to target'
                Copy-Item -Path $FileObjects[$i] `
                          -Destination ((Get-Variable -Name ("DestinationFilePath" + [string]$i)).Value) `
                          -Force
    
                } # END     rename the file instead of overwriting any existing. 
            Write-Debug 'Done copying ... Subtract 1 from $i and loop. '
            $i -= 1
            
            }
        } # END variables for backup file copy operation, so the destination can be different if needed.  
    
    
     Write-Debug "End of function" 
     } # END Function  
    

    Also, It's been a long day for my brain and I may not be choosing the best implementation do accomplish my goals.

    Also wondering if I should I have used a cmdlet instead of a function?

    Thanks. 🙂

  • #27142
    Profile photo of Tim Pringle
    Tim Pringle
    Participant

    Hey Tony,

    Maybe it's the auto formatting of the code, but it's not working for me at all. There are additional curly brackets in the code which are not used for scriptblocks, and this is causing what should be code to be seen as text, which is then output to the screen, e.g.
    Line 6 and its counterpart.

    If it's not how you've written it, and the formatting has added it, could you paste the code in again as normal text?

  • #27160
    Profile photo of Tony Pagliaro
    Tony Pagliaro
    Participant

    Ok yeah, rookie mistake. I was using those { }'s for housekeeping. I just prefaced them all with an Invoke-Command and it's working (sorta) now. Better at least, needs more debugging.

    I'm gonna leave this one open for a day or two, in case I hit another roadblock. It almost seems like I may be reinventing the wheel. I've looked on yonder Internets for a script that does this but nothing I found handles what I want, all told.

    Thanks, Tim..

    Latest code for curious folks:

    function Backup-File ([string]$BkpFolder = "Backup", [string]$FileType = "*")
    {
    $DebugPreference = "Continue" 
    
        # Create target path if it doesn't exist
        Invoke-Command {
        
            if (-not (Test-Path $BkpFolder))
            {
                New-Item -Path $BkpFolder -ItemType directory |out-null 
            }
        } 
        # END ...target path if it doesn't exist
    
        # Master object to variable
        Invoke-Command {
            $FileObjects = Get-ChildItem ("*." + [string]$FileType) | Sort-Object Length -Descending
            
        } 
        # END       ... to variable
    
        # Introduce variables for backup file copy operation, so the destination can be different if needed. 
        Invoke-Command {
            
            
            # [int]$i = $FileObjects.Count - 1 
            
            
            Write-debug ('File variable set $i. ' + " i = $i")
            While ($i -gt -1) {
                Write-Debug ('While $i > -1 ... Setting 3 variables.   ' + " i = $i")
                Set-Variable -Name ("SourceFileObject"      + [string]$i) -Value $FileObjects[$i]
                Write-Debug "SourceFileObject = $SourceFileObject"
                Set-Variable -Name ("DestinationFilename" + [string]$i) -Value $FileObjects[$i].Name
                Write-Debug "DestinationFilename = $DestinationFilename"
                Set-Variable -Name ("DestinationFilePath" + [string]$i) -Value (".\" + ($BkpFolder) + "\" + ($FileObjects[$i]).Name)
                Write-Debug "DestinationFilePath = $DestinationFilePath"
    
                Write Debug "Incrementally rename $DestinationFilePath instead of overwriting any existing. "
                Invoke-Command {
                    Write Debug "Check to see if $DestinationFilePath matches the filename of $FileObjects[$i]."
                    If (Test-Path -Path ((Get-Variable -Name ("DestinationFilePath" + [string]$i)).Value)) 
                     {
                    Write-Debug 'If $DestinationFilename# exists in target ... find a new name'
                        # Iterate until you have a unique filename
                        $j = 2
                        While (Test-Path -Path ((Get-Variable -Name ("DestinationFilePath" + [string]$i)).Value)) 
                        {
                             Write-Debug 'While $DestinationFilename# exists in target ... change name'
                             $copyJ = (" (Copy " + [string]$j + ")")
                             Set-Variable -Name ("DestinationFilePath" + [string]$i) `
                                          -Value (".\" + ($BkpFolder) + "\" `
                                                   + ($FileObjects[$i].Name).TrimEnd($FileObjects[$i].Extension) `
                                                   + $copyJ + $FileObjects[$i].Extension)
                        $j += 1
                        } 
                    }
                Write-Debug 'Copying file to target'
                Copy-Item -Path $FileObjects[$i] `
                          -Destination ((Get-Variable -Name ("DestinationFilePath" + [string]$i)).Value) `
                          -Force
    
                } 
                # END       ... rename the file instead of overwriting any existing. 
            Write-Debug 'Done copying ... Subtect 1 from $i and loop. '
            $i -= 1
            
            }
        } 
        # END                 ... backup file copy operation, so the destination can be different if needed.  
    
    
     Write-Debug "End of function" 
     } # END Function  
    
  • #27168
    Profile photo of Rob Simmers
    Rob Simmers
    Participant

    Your script appears to be overcomplicated with all of the Invoke-Commands and Set and Get-Variables. Take a look at this approach and see if it meets your requirements:

    $source = "C:\Test\*"
    $destination = "C:\Backup"
    
    if (!(Test-Path -Path $destination)) {
        #Create the backup directory if doesn't exist
        New-Item -Path $destination -ItemType Directory -Force
        #Also create a text file so $destinationFiles isn't null
        New-Item -Path $destination -Name "test.txt" -Force -ItemType File
    }
    
    #Get files from the source and desination paths
    $sourceFiles = Get-ChildItem -Path $source -Include "*.txt"
    $destinationFiles = Get-ChildItem -Path ($destination + "\*") -Include "*.txt"
    
    #Compare the source and destination and if exists only in the source, copy it to the destination 
    Compare-Object -ReferenceObject $sourceFiles -DifferenceObject $destinationFiles -Property FullName -PassThru |
    Where {$_.SideIndicator -eq "< ="} |
    foreach {
        Copy-Item -Path $_.FullName -Destination ($destination + "\" + $_.Name) -WhatIf
    }
    
  • #27173
    Profile photo of Tony Pagliaro
    Tony Pagliaro
    Participant

    Thanks Rob! I didn't see your reply there before reworking my script from scratch myself. You're right. It looks a lot better now. Added some Verbose language as well.

    Can't figure out how to post the full function. There are 's that end up screwing up the code. If there's a trick to that, someone pls let me know.

    Edit: apparently it's a wordpress issue with backticks..

You must be logged in to reply to this topic.