Aug
15
2011

Backup files with Powershell and 7-Zip

I had the need today to backup files from one network location to another using 7-Zip to compress the files. Actually I could have used any program, I chose 7-Zip because it's free and it works great. The objective is to backup the files modified within the last day from one location to another while keeping the folder structure the same. You can download 7-Zip from here. And I came across a nice article with good examples how to use the command line parameters. A simple syntax to use 7-Zip from command prompt would be something like this:

CD\
CD\Program Files\7-Zip\
7z a -t7z \\myserver1\targetshare\"%DATE:~4,2%.%DATE:~7,2%.%DATE:~-4% Backup".7z \\myserver2\sourceshare -mmt

Where you can save this in a bat file and schedule it to run on a ragular basis. However my objective is more complicated because I have to keep the file structure the same and only backup files that are newer than a certain date. That's why I decided to use the abilities of Powershell.

So what I decided to do is create a function that I can call recursively:

Backup individual files with version:

function BackupFiles ($source, $target )
{
  $compareDate = (Get-Date).AddDays(-1)
  $children = get-childitem -path $source | where-object {$_.lastwritetime -gt $compareDate}
  foreach ($child in $children) 
  {
   if($child.Name.Length -gt 0)
   {
      if ($child.PSIsContainer)
      {
        $newTarget = join-path -path $target -childpath $child.Name
        if (Test-Path $newTarget)
        {
            BackupFiles $child.FullName $newTarget
        }
        else
        {
            New-Item $newTarget -type directory
            BackupFiles $child.FullName $newTarget
        }        
      }
      else 
      {
        $backupFile = join-path -path $target -childpath $child.Name
        $backupFile = GetFileName $backupFile 0
        &'C:\Program Files\7-Zip\7z' 'a' '-t7z' $backupFile'.7z' $child.FullName '-mmt'
      }
    }
  }
}
function GetFileName($file,[int]$index)
{
    $newFile = $file     
    if($index -gt 0)
    {
        $newFile = $file + "-" + $index 
    }
    $testFile = $newFile + '.7z'    
    if (Test-Path $testFile)
    {
        $nindex = $index + 1
        return GetFileName $file $nindex
    }
    else
    {
        return $newFile
    }
}
BackupFiles "\\myserver1\sourceShare" "\\myserver2\targetShare"

There are a few key areas:

PSIsContainer defines the child as a container or a folder vs. a file Read More.

Join-path is used to concatenate file system paths Read More

New-Item is used to create a new directory if it doesn't exist Read More.

Next step is to save this to a .ps1 file and schedule it from the Tast Scheduler. You might run into a problem though. By default the system will not allow you to run scripts so you have to run the code below Read More in PowerShell:

Set-ExecutionPolicy RemoteSigned

Then create a new task in Task Scheduler and presuming you save the file in c:\ScheduledTask\backup.ps1 run this daily:

powershell -command "& {C:\ScheduledTasks\Backup.ps1}"

I also needed to backup files from a location where I still had to keep the folder structure the same but the files within the folders had to be compressed into one file:

function BackupFiles ($source, $target )
{
    $newSource = join-path -path $source -childpath '*.*'
    $dir = get-item $target
    $newTarget = join-path -path $target -childpath $dir.Name
    #Write-output $newTarget
    #Write-output $newSource
    &'C:\Program Files\7-Zip\7z' 'a' '-t7z' $newTarget'.7z' $newSource '-mmt'
        
    $children = get-childitem -path $source 
    foreach ($child in $children) 
    {
        if($child.Name.Length -gt 0)
        {
            if ($child.PSIsContainer)
            {
                $newTarget = join-path -path $target -childpath $child.Name
                #Write-output $newTarget
                if (Test-Path $newTarget)
                {
                    BackupFiles $child.FullName $newTarget
                }
                else
                {
                    New-Item $newTarget -type directory
                    BackupFiles $child.FullName $newTarget
                }
            }
        }
    }
}
BackupFiles "\\myserver1\sourceShare" "\\myserver2\targetShare"