Jump to content

Script needed to copy a single file to USB drive but only keep the last 3 versions


Recommended Posts

Posted

Hello,

 

I assume someone has probably done something like this before and and can just paste what they have done, as I am no good at scripts.

 

Twice a week I need to copy a file on our backup server to a USB drive. The USB drive is only large enough to store 3 of the backup files. I have been doing this manually until now with folder 1,2, & 3 on the USB drive and I copy the file which is in a folder on the servers C drive to one of the folders on the USB drive.

 

I assume a little script and task scheduler should be able to achieve the same thing.

 

BTW the file on the server I need to copy is - C:\iSCSIVirtualDisks\man-vd.vhdx and the USB drive is allocated to the E drive.

 

Many thanks

Posted (edited)

[color=#696969][font=Monaco]$file="C:\Scripts\Count.txt"

$count=Get-Content$file
$1=1
$2=2
$3=3

if($count-eq1){
 Write-Host"Backup 1"
 Copy-Item"C:\iSCSIVirtualDisks\man-vd.vhdx"-Destination "E:\Folder 1"
 Clear-Content$file
 $2|Set-Content$file
} elseif($count-eq2){
 Write-Host"Backup 2"
 Copy-Item"C:\iSCSIVirtualDisks\man-vd.vhdx"-Destination "E:\Folder 2"
 Clear-Content$file
 $3|Set-Content$file
} else {
 Write-Host"Backup 3"
 Copy-Item"C:\iSCSIVirtualDisks\man-vd.vhdx"-Destination "E:\Folder 3"
 Clear-Content$file
 $1|Set-Content$file
}

[/font][/color]

 

This should do what you need, it reads a number from the text file and then backups to that number. When the script finishes it changes the number in the text file so the next time the script runs it will point to the next location and will loop everytime it runs.

 

Hope it helps, let me know if you use it or need help understanding it.

Edited by ShellfishClive
  • Thanks 1
Posted

Hmmm can't get the script to work. I created C:\Scripts\Count.txt and made sure I had Folder 1 etc in the E drive.

 

What should I save the script as. I used script.bat

 

- - - Updated - - -

 

oooops hold on

Posted

OK so testing on windows 10 pc & just using a txt file as the test file to copy it does not work.

 

Deleting everything and just having this one line still does not work

 

Copy-Item"C:\iSCSIVirtualDisks\man-vd.txt"-Destination "E:\Folder 1"

 

What am I doing wrong?

  • Thanks 1
Guest Guest
Posted
Your command is not correct it should be: copy-item -path “c:\iscsivirtualdisks\man-vf.txt” - destination “e:\folder 1”
Posted (edited)

-Path shouldn't matter too much as it knows the first directory is the one that the file should move from.

 

Try running it as admin in ISE mode.

 

In the count.txt file put a 1 in there that predefined as the first number, if it's still erroring would you be able to send me the error message that you are having, it's working fine for me.

 

Sorry for the late reply.

 

Edit:

 

I have attached the file here: Backup Script.ps1

 

I believe the copying and paste method was messing with the formatting of the document which would cause issues.

Edited by ShellfishClive
  • Thanks 1
Posted

I created an AutoIT script that will create a list of all files in question, and get the last modified time from them , sort them from oldest to newest and then isolate the oldest one , delete it, and replace the current one from C:\iSCSIVirtualDisks\man-vd.vhdx to the correct folder. You will need to compile this to an exe via AutoIT and https://www.autoitscript.com/cgi-bin/getfile.pl?autoit3/autoit-v3-setup.exe and you will need the editor https://www.autoitscript.com/cgi-bin/getfile.pl?../autoit3/scite/download/SciTE4AutoIt3.exe

 

You can run this by pressing F5 in after saving the contents of this script into a txt file and renaming to .au3 file

You can test it with other file types if you wish

 

There are comments listed in here and things can be commented out to not display information

I am presenting the version that has the comments in and another without comments and you are satisfied with the results.

 

I am not sure if this is something you would be interested in working with or not but thought I would provide what I came up with.

 

 

Use this one to see the results

#RequireAdmin
#include 
#include 
Dim $b[1][2]
$filepath = 'E:\' ; this will bethe file/folder path
$a = _FileListToArrayRec($filepath, '*.vhdx', $FLTAR_FILES, $FLTAR_RECUR, "", $FLTAR_FULLPATH) ; creates a list of all files within all subfolders of files with the extension .vhdx. YOU WILL HAVE TO CHANGE THE EXTENSION AFTER YOU ARE SATISIFIED
_ArrayDisplay($a); displays current files with .vhdx extension remove, add a ; in front of _Arraydisplay to comment it out
For $i = 1 To UBound($a) - 1 ; creates a loop of 1 to $a , $a being how many files are in the list
   $c = FileGetTime($a[$i], $FT_MODIFIED, $FT_STRING) ; gets last modified time the format is as follows: YYYYMMDDHHMMSS and sets to the variable $c
   $d = $a[$i] ; sets the full file path to the variable $d
   $fill = $d & "|" & $c ; creates a multi-dimensional array with the full file path in one column and the last modified time in the second column
   _ArrayAdd($b, $fill) ; adds it to the array
Next
_ArrayDisplay($b) ;displays current list
_ArraySort($b, 0, 0, 0, 1) ; sorts the second column of files assending so the oldest will be on top
_ArrayDisplay($b) ; displays the sorted list
MsgBox(0, "this is the oldest file on the list", $b[1][0]) ; lists the oldest file
$f = StringSplit($b[1][0], '\'); splits the string based on '\' so we can isolate the folder
$j = $filepath & $f[uBound($f) - 2]; rebuilds the file path for the replacement file minus the file name
MsgBox(0, "folder path for new file", $j); displays the folder where the new file is going to go this can be commented out when compiled.
;FileDelete($b[1][0]) ; deletes the oldest file
;FileCopy('C:\iSCSIVirtualDisks\man-vd.vhdx', $b[1][0],1)

 

 

Use this one if out are satisfied and ready for a test

#RequireAdmin
#include 
#include 
Dim $b[1][2]
$filepath = 'E:\' ; this will be E:\ in your case
$a = _FileListToArrayRec($filepath, '*.vhdx', $FLTAR_FILES, $FLTAR_RECUR, "", $FLTAR_FULLPATH) ; creates a list of all files within all sub-folders of files with the extension .vhdx. YOU WILL HAVE TO CHANGE THE EXTENSION AFTER YOU ARE SATISIFIED
;_ArrayDisplay($a); displays current files with .vhdx extension remove, add a ; in front of _Arraydisplay to comment it out
For $i = 1 To UBound($a) - 1 ; creates a loop of 1 to $a , $a being how many files are in the list
   $c = FileGetTime($a[$i], $FT_MODIFIED, $FT_STRING) ; gets last modified time the format is as follows: YYYYMMDDHHMMSS and sets to the variable $c
   $d = $a[$i] ; sets the full file path to the variable $d
   $fill = $d & "|" & $c ; creates a multi-dimensional array with the full file path in one column and the last modified time in the second column
   ;_ArrayAdd($b, $fill) ; adds it to the array
Next
;_ArrayDisplay($b) ;displays current list
;_ArraySort($b, 0, 0, 0, 1) ; sorts the second column of files assending so the oldest will be on top
;_ArrayDisplay($b) ; displays the sorted list
;MsgBox(0, "this is the oldest file on the list", $b[1][0]) ; lists the oldest file
$f = StringSplit($b[1][0], '\'); splits the string based on '\' so we can isolate the folder
$j = $filepath & $f[uBound($f) - 2]; rebuilds the file path for the replacement file minus the file name
;MsgBox(0, "folder path for new file", $j); displays the folder where the new file is going to go this can be commented out when compiled.
FileDelete($b[1][0]) ; deletes the oldest file
FileCopy('C:\iSCSIVirtualDisks\man-vd.vhdx', $b[1][0],1)

  • Thanks 1
Posted (edited)

I hope you don't mind another PowerShell script? I thought I would have a go too. :)

 

#Requires -Version 3.0

$Source      = "C:\iSCSIVirtualDisks\man-vd.vhdx"
$Destination = "E:\"
$TargetDir   = "{0}{1}" -f $Destination, ((Get-ItemProperty $Source).LastWriteTime).ToString("yyyy-MM-dd HH.mm.ss")

If (-not(Test-Path "$TargetDir")) { $Null = New-Item "$TargetDir" -Type Directory; Copy-Item -Path "$Source" -Destination "$TargetDir" -ErrorAction Stop -Verbose }
Get-ChildItem -Path $Destination -Directory | Where-Object { $_.Name -match "^\d{4}-\d{2}-\d{2} \d{2}\.\d{2}\.\d{2}$"} | Sort-Object LastWriteTime -Descending | Select-Object -Skip 3 | Remove-Item -Force -Recurse

 

This script copies man-vd.vhdx to a subfolder on your USB drive based on the VHDXs modified date and keeps the last three most recent backups. To save time, if the VHDX doesn't change the file isn't copied again.

 

The main advantages to my script is that you don't require any additional software and it doesn't write any files to the C: drive.

 

I have tested it on Windows 10 and it worked perfectly.

Edited by Arthur
  • Thanks 1
Posted

Its amazing how many different ways there are to achieve the same goal.

 

One question, with @Arthur script, when all 3 folders and files are on the USB drive will it delete the oldest folder before it starts to copy the forth folder or does it delete it after it's copied the fourth folder? I can see the remove-item but this is at the end of the script, hence why I am asking.

Posted (edited)
when all 3 folders and files are on the USB drive will it delete the oldest folder before it starts to copy the forth folder or does it delete it after it's copied the fourth folder? I can see the remove-item but this is at the end of the script, hence why I am asking.

Good question. The script in post #14 will delete the oldest folder after the .vhdx has been copied which means there would be a (temporary) fourth folder.

 

It sounds like there may not be room for the fourth folder so what you could do is do the deletion before the copy and change the "Select-Object -Skip" from 3 to 2 so there will always be enough disk space to copy the third .vhdx. i.e.

 

#Requires -Version 3.0

$Source      = "C:\iSCSIVirtualDisks\man-vd.vhdx"
$Destination = "E:\"
$TargetDir   = "{0}{1}" -f $Destination, ((Get-ItemProperty $Source).LastWriteTime).ToString("yyyy-MM-dd HH.mm.ss")

Get-ChildItem -Path $Destination -Directory | Where-Object { $_.Name -match "^\d{4}-\d{2}-\d{2} \d{2}\.\d{2}\.\d{2}$"} | Sort-Object LastWriteTime -Descending | Select-Object -Skip 2 | Remove-Item -Force -Recurse
If (-not(Test-Path "$TargetDir")) { $Null = New-Item "$TargetDir" -Type Directory; Copy-Item -Path "$Source" -Destination "$TargetDir" -ErrorAction Stop -Verbose }

 

However, the issue with this is that because the backup folders are based on the the last modified date/time of the .vhdx file you could end up with only two backups the next time the script is run if the file doesn't change.

 

To solve that you could create the folders based on the date/time the script is run...

 

#Requires -Version 3.0

$Source      = "C:\iSCSIVirtualDisks\man-vd.vhdx"
$Destination = "E:\"
$TargetDir   = "{0}{1}" -f $Destination, (Get-Date).ToString("yyyy-MM-dd HH.mm.ss")

Get-ChildItem -Path $Destination -Directory | Where-Object { $_.Name -match "^\d{4}-\d{2}-\d{2} \d{2}\.\d{2}\.\d{2}$"} | Sort-Object LastWriteTime -Descending | Select-Object -Skip 2 | Remove-Item -Force -Recurse
If (-not(Test-Path "$TargetDir")) { $Null = New-Item "$TargetDir" -Type Directory; Copy-Item -Path "$Source" -Destination "$TargetDir" -ErrorAction Stop -Verbose }

 

This obviously has the opposite issue (which may/may not be a problem in your case?) whereby if the .vhdx doesn't change, each time the script is run you could potentially end up with three backups with identical .vhdx files. :)

Edited by Arthur
  • 3 weeks later...
Posted

BTW I went with option 1 of the two scripts in #18

 

Even though it does delete the 3rd backup if the vhdx as not changed, I still have two I can access. Option 2 will eventually overwrite all the backups if the vhdx does not change, and if that happened to be a faulty vhdx file, that could be an issue.

Posted
Whats the best way of setting up task scheduler to run this script, do I need to run powershell as a task?

I'd save it as a .ps1 file then a scheduled task to run powershell.exe with scriptname.ps1 as the parameter (unless there's a special powershell section these days in which case do it that way)

  • 2 weeks later...
Posted (edited)

Hi, me again. When the script runs manually it creates the backup file on the USB drive, but the file size of the file is the full size it will eventually be, this is OK if I run the script manually as the Powershell cmd windows remains open for the duration of the copy process, EDIT so when the cmd window closes I can tell it has actually finished.

 

When the script runs using task scheduler the task displays running for a second and then goes back to ready, there is no Powershell window cmd open and I have no idea if the script is still copying. I can see the new folder has been created and the full sized container file has been created.

 

BTW - the copy process should take about 5 hours.

 

Is there anyway of knowing if the task / script is running?

Edited by TwistedHelixis
Posted
Is there anyway of knowing if the task / script is running?

In Task Scheduler does the task have a status of 'Running' or 'Ready'? You could also check Task Manager to see if PowerShell.exe is running there.

 

What do you have for your scheduled task action commands? e.g.

 

[b]Action[/b] = Start a program

[b]Program/script[/b]
C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe

[b]Add arguments (optional)[/b]
-NonInteractive -NoLogo -NoProfile -File "X:\Path\To\Script.ps1"

  • Thanks 1
Posted

I have just gone back on the server and it does now show the task as running.

 

I set task as follows, should I use the parameters supplied by @Arthur ?

 

Program/script

powershell

 

Add arguments (optional)

X:\Path\To\Script.ps1

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now



×
×
  • Create New...