Powershell robocopy /mir Copy Files before /mir deletes - powershell

I wrote a Powershell script which does following Steps.
It RoboCopies Files from a Server to an External Hard drive (incremental backup)
Next time it is supposed to check if any files were deleted on the Server, if so move those Files from the Backup Folder to a Folder Called _DeletedFiles in the Backup Hard drive.
RoboCopy with /MIR (which will delete the files which are deleted on the server also on the backup, and that's okay because I saved them already on the _DeletedFiles Folder)
Point of this _DeletedFiles Folder is that even if someone deleted a file we want to keep it somewhere for at least 60 Days.
Now this script is a little bit more complex including writing to log file, testing paths, and first run if statement etc.
All seems to work except the step where I want to copy the Files which are deleted on the Server from the BackUp to a new Folder.
This step looks similar to this:
$testfolder = Test-Path "$UsbDisk\$backupFolder"
# If a Backup folder already exist then Compare files and see if any changes have been made
if ( $testfolder -eq $true ) { #IF_2
# Copy deleted files to BackUp Folder
MyLog "Check for Deleted Files on E:\" 0
$source = "E:\"
$sourcelist = Get-ChildItem $source -Recurse
$destination = "$UsbDisk\$backupFolder\Data_01\_DeletedFiles"
foreach ($file in $sourcelist){
$result = test-path -path "E:\*" -include $file
if ($result -like "False"){
Copy-Item $file -Destination "$destination"
}
}
# Start Synchronizing E:\
MyLog "Start Synchronizing E:\" 0
Robocopy "E:\" "$UsbDisk\$backupFolder\Data_01" /mir /r:2 /w:3 /M /XD VM_*
MyLog "E:\ is up to Date" 0
# Copy deleted files to BackUp Folder
MyLog "Check for Deleted Files on F:\" 0
$source = "F:\"
$sourcelist = Get-ChildItem $source -Recurse
$destination = "$UsbDisk\$backupFolder\Data_02\_DeletedFiles"
foreach ($file in $sourcelist){
$result = test-path -path "F:\*" -include $file
if ($result -like "False"){
Copy-Item $file -Destination "$destination"
# Then Delete it from the BackUp
}
}
# Start Synchronizing F:\
MyLog "Start Synchronizing F:\" 0
Robocopy "F:\" "$UsbDisk\$backupFolder\Data_02" /mir /r:2 /w:3 /M /XD VM_*
MyLog "F:\ is up to Date" 0
}
The error I get that files can't be copied because they do not exist at the destination; however, it tries to copy files which shouldn't be copied in the first place.
I wonder if anyone has an idea to solve this more elegant, or how to fix my code snip.

I think the problem may be in the test-path commands. I would replace the include $file with include $file.Name. The include parameter expects a string, not an object.
And in the interests of code maintainability I would also replace ($result -like "False") with (-not $result). This is because $result will contain a boolean value ($true or $false), not a string.

Related

Copy-Item with overwrite?

Here is a section of code from a larger script. The goal is to recurse through a source directory, then copy all the files it finds into a destination directory, sorted into subdirectories by file extension. It works great the first time I run it. If I run it again, instead of overwriting existing files, it fails with this error on each file that already exists in the destination:
Copy-Item : Cannot overwrite the item with itself
I try, whenever possible, to write scripts that are idempotent but I havn't been able to figure this one out. I would prefer not to add a timestamp to the destination file's name; I'd hate to end up with thirty versions of the exact same file. Is there a way to do this without extra logic to check for a file's existance and delete it if it's already there?
## Parameters for source and destination directories.
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
# Build list of files to sort.
$Files = Get-ChildItem -Path $Source -Recurse | Where-Object { !$_.PSIsContainer }
# Copy the files in the list to destination folder, sorted in subfolders by extension.
foreach ($File in $Files) {
$Extension = $File.Extension.Replace(".","")
$ExtDestDir = "$Destination\$Extension"
# Check to see if the folder exists, if not create it
$Exists = Test-Path $ExtDestDir
if (!$Exists) {
# Create the directory because it doesn't exist
New-Item -Path $ExtDestDir -ItemType "Directory" | Out-Null
}
# Copy the file
Write-Host "Copying $File to $ExtDestDir"
Copy-Item -Path $File.FullName -Destination $ExtDestDir -Force
}
$Source = "C:\Temp"
$Destination = "C:\Temp\Sorted"
You are trying to copy files from a source directory to a sub directory of that source directory. The first time it works because that directory is empty. The second time it doesn't because you are enumerating files of that sub directory too and thus attempt to copy files over themselves.
If you really need to copy the files into a sub directory of the source directory, you have to exclude the destination directory from enumeration like this:
$Files = Get-ChildItem -Path $Source -Directory |
Where-Object { $_.FullName -ne $Destination } |
Get-ChildItem -File -Recurse
Using a second Get-ChildItem call at the beginning, which only enumerates first-level directories, is much faster than filtering the output of the Get-ChildItem -Recurse call, which would needlessly process each file of the destination directory.

Sync Folders with Powershell - New and edited Files

I am trying to Sync 2 Folders with Powershell.
Comparing and copying any new Files works just fine. But I want to additionally copy all files that got modified in the reference Foler.
The following Code works and copys all new Files which got created in the reference Folder.
$folderReference = 'C:\Users\Administrator\Desktop\TestA'
$folderToSync = 'C:\Users\Administrator\Desktop\TestB'
$referenceFiles = Get-ChildItem -Recurse -Path $folderReference
$FolderSyncFiles = Get-ChildItem -recurse -Path $folderToSync
$fileDiffs = Compare-Object -ReferenceObject $referenceFiles -DifferenceObject $FolderSyncFiles
foreach ($File in $fileDiffs){
try {
if ($File.SideIndicator -eq "<="){
$FullSourceObject = $File.InputObject.Fullname
$FullTargetObject = $File.InputObject.Fullname.Replace($folderreference, $folderToSync)
Write-Host "copy File: " $FullSourceObject
copy-Item -Path $FullSourceObject -Destination $FullTargetObject
}
}
catch {
Write-Error -Message "Something went wrong!" -ErrorAction Stop
}
}
Now I also want to copy the modified Files.
I tried -property LastWriteTime after the Compare-Objectbut I get a WriteErrorException when running the code.
Do you guys have some tips on how to get this Code to run properly?
Thanks in advance
I'd just use robocopy, it's built specifically for this type of task and included in most modern versions of windows by default:
robocopy C:\Source C:\Destination /Z /XA:H /W:5
/Z - resumes copy if interrupted
/XA:H - ignores hidden files
/W:5 - shortens wait for failures to 5 sec (default 30)
Worth taking a look through the documentation as there's many different options for practically every situation you can think of...
For example, add /MIR and it will remove any files from the destination when they are deleted from source.

custom robocopy script in PowerShell

I would like to move all .txt files present in a source directory (including .txt present in subfolders) to a destination directory.
For eg:
Source directory: D:\OFFICE work\robtest\test1
Above directory also contains many subfolders.
Destination directory: D:\OFFICE work\robtest\test2
I would like to move only .txt files in source dir to the above mentioned destination directory, in such a way that only 3 .txt files must be present per sub folder (includes random folder creation) in the destination directory.
Below is the code I tried, but PowerShell says
The filename, directory name, or volume label syntax .(D:\OFFICE work\robtest\test1\testtest\testtest2\
New folder\test01112.txt\ ) is incorrect.
I am not sure why there is extra '/' after the above path in robocopy.
$fileperfolder = 3
$source = "D:\OFFICE work\robtest\test1";
$dest = "D:\OFFICE work\robtest\test2";
$folderName = [System.IO.Path]::GetRandomFileName();
$fileList = Get-ChildItem -Path $source -Filter *.txt -Recurse
Write-Host $fileList
$i = 0;
foreach ($file in $fileList) {
$destiny = $dest + "\" + $folderName
Write-Host $file.FullName;
Write-Host $destiny;
New-Item $destiny -Type Directory -Force
robocopy $file.FullName $destiny /mov
$i++;
if ($i -eq $fileperfolder) {
$folderName = [System.IO.Path]::GetRandomFileName();
$i = 0;
}
}
You're getting the error (and the "directory" in the output) because robocopy expects two folders as the first and second argument, optionally followed by a list of filename specs, but you're providing the path to a file as the source.
From the documentation (emphasis mine):
Syntax
robocopy <Source> <Destination> [<File>[ ...]] [<Options>]
Parameters
<Source> Specifies the path to the source directory.
<Destination> Specifies the path to the destination directory.
<File> Specifies the file or files to be copied. You can use wildcard characters (* or ?), if you want. If the File parameter is not specified, *.* is used as the default value.
<Options> Specifies options to be used with the robocopy command.
Since you're moving one file at a time and apparently don't want to maintain the directory structure I'd say robocopy isn't the right tool for what you're doing anyway.
Replace
robocopy $file.FullName $destiny /mov
with
Move-Item $file.FullName $destiny
and the code should do what you want.
robocopy $file.Directory $destiny $file /mov
solved this issue.

Copy folder structure and content of specific folders

So I've found xcopy super helpful to copy entire folder structures. But I also need to copy the contents of specific folders into the new directories as well.
For example:
1. C:\OriginalDir
- \This
* \Test
- \That
* \Test
- \Other
I can use: xcopy C:\OriginalDir C:\TempDir /e /t to copy the entire structure of the C:\OriginalDir. However, I also need to copy the contents of both \Test folders into the new directory as well. I'm fairly new to xcopy and I've also looked into robocopy. Is there a way to do this? I'm trying to accomplish this in powershell and thought about iterating through the folder structure, but that still doesn't store the parent folder structure when I finally reach the Test folder.
Thanks.
Have you tried robocopy - for example:
$source = "C:\Your\Source\Directory"
$dest = "C:\Your\Destination\Directory"
robocopy $source $dest /e
The 'e' switch will copy subdirectories and their contents (including empty subdirectories).
If you wanted to exclude the \Other directory (it's not entirely clear from your question), you could do the following:
robocopy $source $dest /e /xf *
(This just copies the directory structure with no files copied)
robocopy $source $dest /XD C:\Other /e
(This copies files, but excludes the named directories)
You can find more information here:
https://technet.microsoft.com/en-GB/library/cc733145.aspx
Edit:
In order to only copy directories beginning with 'Test', you could do the following:
$exclude = gci C:\OriginalDir -ad | ?{ $_.Name -notlike 'Test*'
robocopy $source $dest /XD $exclude /e
If your folder structure is more than one level deept, you could use the -recurse switch on Get-Childitem
Thanks to Steve for getting me started and getting me thinking about this correctly. Ended up scripting it out manually without using RoboCopy or Xcopy as I could not get them to work exactly how I wanted to.
$target = "C:\\TestTemp"
foreach($item in (Get-ChildItem "C:\\OriginalDir\\This" -Recurse)){
if ($item.PSIsContainer -and ($item.Name -eq "obj" -or $item.Name -eq "bin")){
$tempPath = $target
$path = $item.FullName
$trimmed = $path.TrimStart("C:\\OriginalDir")
$pieces = $trimmed.Split("\\");
foreach($piece in $pieces){
$tempPath = "$tempPath\$piece"
New-Item -ItemType Directory -Force -Path "$tempPath"
if($piece -eq "Test" -or $piece -eq "Temp"){
Copy-Item -path $path\** -Destination $tempPath -Recurse -force
}
}
}
}

Powershell restore previous version of the files

We got hit by virus, it changed all common file extension to .kmybamf (.txt >> .txt.kmybamf ) and if I delete .kmybamf , the file got damaged.....
So I made a list of all files that got damaged. now I'm trying to overwrite them by previous version. Anyone knows how to do it in Powershell?
I can do it in cmd similar to this
subst X: \localhost\D$\#GMT-2011.09.20-06.00.04_Data
robocopy Z: D:\Folder\ /E /COPYALL
But I want to do it in one shot in Powershell, It has to be a "if .kmybamf found, then restore previous version." and powershell seems like has no such cmdlet for restoring previous version of files or folders.
$fileList = Get-Content -Path "\\localhost\D$\#GMT-2011.09.20-06.00.04_Data"
$destinationFolder = "D:\Folder\"
foreach ($file in $fileList)
{
Copy-Item -Path $file -Destination $destinationFolder -Force
}
This will also work but I find it less readable
Get-Content -Path "\\localhost\D$\#GMT-2011.09.20-06.00.04_Data" | ForEach-Object { Copy-Item -Path $_ -Destination "D:\Folder" -Force }
Get-Content is for reading the text from the files, to read the files from a folder you would have to use Get-Childitem