0
I am trying to create a backup database on a network drive using fso.folder copy. My intention is to move all files and subfolder within the folder, but if a file already exists on the backup drive, skip it, and copy the remainder of the files in the folder.
FSO.copyfolder Source:=Sourcefilename, Destination:=Destinfilename, OverwriteFiles:= False
However, the script errors when it finds the existing file. Any advice would be appreciated.
Please, try the next code:
Sub testCopyFolder()
Dim FSO As Object, SourceFold As String, DestinationFold As String
SourceFold = "Source folder path" ' ending in "\"
DestinationFold = "Destination folder path" ' ending in "\"
Set FSO = CreateObject("Scripting.FileSystemObject")
If Not FSO.FolderExists(DestinationFold) Then
FSO.CopyFolder SourceFold, DestinationFold
End If
End Sub
You can proceed in a similar way in order to copy files. Of course, using FSO.FileExists()...
Backup Folder and Its Subfolders Without Overwriting
The following will backup a Source Folder to a Destination Folder i.e. copy missing folders and files.
TESTcopyFolder is just an example how you could use the solution.
It will call the initializing procedure, backupFolder, which will call backupFolderCopy and backupFolderRecurse when necessary.
The declaration Private SkipPath As String and the three procedures have to be copied to the same (usually standard) module, e.g. Module1.
The Code
Option Explicit
Private SkipPath As String
Sub TESTcopyFolder()
Const srcPath As String = "F:\Test\2020\65412587\Test1"
Const dstPath As String = "F:\Test\2020\65412587\Test2"
backupFolder srcPath, dstPath
' Open Destination Path in File Explorer.
'ThisWorkbook.FollowHyperlink dstPath
End Sub
' Initialize
Sub backupFolder( _
ByVal srcPath As String, _
ByVal dstPath As String, _
Optional ByVal backupSubFolders As Boolean = True)
Dim fso As Object: Set fso = CreateObject("Scripting.FileSystemObject")
With fso
If .FolderExists(srcPath) Then
backupFolderCopy fso, srcPath, dstPath
If backupSubFolders Then
SkipPath = ""
backupFolderRecurse fso, srcPath, dstPath
End If
MsgBox "Backup updated.", vbInformation, "Success"
Else
MsgBox "Source Folder does not exist.", vbCritical, "No Source"
End If
End With
End Sub
' Copy Folders
Private Function backupFolderCopy( _
fso As Object, _
ByVal srcPath As String, _
ByVal dstPath As String) _
As String
With fso
If .FolderExists(dstPath) Then
Dim fsoFile As Object
Dim dstFilePath As String
For Each fsoFile In .GetFolder(srcPath).Files
dstFilePath = .BuildPath(dstPath, fsoFile.Name)
' Or:
'dstFilePath = Replace(fsoFile.Path, srcPath, dstPath)
If Not .FileExists(dstFilePath) Then
.CopyFile fsoFile.Path, dstFilePath
End If
Next fsoFile
'backupFolderCopy = "" ' redundant: it is "" by default.
Else
.CopyFolder srcPath, dstPath
backupFolderCopy = srcPath
End If
End With
End Function
' Copy SubFolders
Private Sub backupFolderRecurse( _
fso As Object, _
ByVal srcPath As String, _
ByVal dstPath As String)
Dim fsoFolder As Object: Set fsoFolder = fso.GetFolder(srcPath)
Dim fsoSubFolder As Object
Dim srcNew As String
Dim dstNew As String
For Each fsoSubFolder In fsoFolder.SubFolders
srcNew = fsoSubFolder.Path
dstNew = fso.BuildPath(dstPath, fsoSubFolder.Name)
' Or:
'dstNew = Replace(srcNew, srcPath, dstPath)
If Len(SkipPath) = 0 Or Left(srcNew, Len(SkipPath)) <> SkipPath Then
SkipPath = backupFolderCopy(fso, srcNew, dstNew)
backupFolderRecurse fso, srcNew, dstNew
End If
Next
End Sub
Related
I have a question about reading files within folders. I found this code:
sub sample()
Dim FileSystem As Object
Dim HostFolder As String
HostFolder = "C:\"
Set FileSystem = CreateObject("Scripting.FileSystemObject")
DoFolder FileSystem.GetFolder(HostFolder)
end sub
Sub DoFolder(Folder)
Dim SubFolder
For Each SubFolder In Folder.SubFolders
DoFolder SubFolder
Next
Dim File
For Each File In Folder.Files
' Operate on each file
Next
End Sub
But how would you go about avoiding a specific folder which is within the original one?
Let's say the you have a folder A which in turn has several folder Bs. within these folders, there are the file required but also another folder, always with the same name, let's say C.
How would you filter out folders Cs?
Thank you for your time
You can try something like this:
' List of complete path of files in folder / subfolders
' Needs to add "Microsoft Scripting Runtime" reference to your file
Sub FolderFilesPath(ByVal pFolder As String, ByRef pColFiles As Collection, _
Optional ByVal pGetSubFolders As Boolean, Optional ByVal pFilter As Collection)
Dim sFolder As String
Dim oFSO As New FileSystemObject
Dim oFolder, oSubFolder As Folder
Dim oFile As File
sFolder = IIf(Right(pFolder, 1) <> "\", pFolder & "\", pFolder)
Set oFolder = oFSO.GetFolder(sFolder)
If Not ExistsInCollection(pFilter, sFolder) Then
For Each oFile In oFolder.Files
pColFiles.Add oFile
Next oFile
If pGetSubFolders Then
For Each oSubFolder In oFolder.SubFolders
FolderFilesPath oSubFolder.Path, pColFiles, pGetSubFolders, pFilter
Next
End If
End If
End Sub
' Vba collection contains
Function ExistsInCollection(col As Collection, key As Variant) As Boolean
On Error GoTo err
ExistsInCollection = True
IsObject (col.Item(key))
Exit Function
err:
ExistsInCollection = False
End Function
'------------------------------------------------------------------------------
Sub TestMe()
Dim colFiles As New Collection, sFilePath As Variant
Dim colExcludedFolders As New Collection
Dim sHostFolder As String
sHostFolder = "C:\temp"
With colExcludedFolders
' add folders you want to exclude
.Add sHostFolder & "\C\", sHostFolder & "\C\"
End With
FolderFilesPath ThisWorkbook.Path, colFiles, True, colExcludedFolders
' colFiles contains filtered files
For Each sFilePath In colFiles
With sFilePath
' do what you want with filtered files
Debug.Print .Path & " - " & .Name & " - " & .DateCreated
End With
Next sFilePath
End Sub
In the following code, I want to access ExcelWorkbook1 declared in Sub OpenExcel in Sub SelectRangeOfCells. Please help me with the same.
Public Sub OpenExcel(ByVal Path As String, ByVal Filename As String)
Dim xlApp As Excel.Application = New Microsoft.Office.Interop.Excel.Application()
xlApp.Visible = True
If xlApp Is Nothing Then
MessageBox.Show("Excel is not properly installed!!")
End If
Dim MyFile As String = Dir$(Path + "\" + Filename)
If MyFile = Filename Then
Dim ExcelWorkbook1 As Workbook = xlApp.Workbooks.Open(Path + "\" + Filename)
Else
MessageBox.Show("Excel not found!")
End If
End Sub
Public Sub SelectRangeOfCells(ByVal Sheet As String, ByVal Range As String)
ExcelWorkbook1.Worksheets(Sheet).activate
End Sub
This shows error in SelectRangeOfCells Sub that ExcelWorkbook1 is not declared.
This is kinda basic.
Of course, you cannot access ExcelWorkbook1 from another sub because it is declared locally in the OpenExcel sub.
To access ExceWorkBook1 using both subs, you must declare it outside of those two subs.
Dim ExcelWorkbook1 As Workbook 'put this here
Public Sub OpenExcel(ByVal Path As String, ByVal Filename As String)
Dim xlApp As Excel.Application = New Microsoft.Office.Interop.Excel.Application()
xlApp.Visible = True
If xlApp Is Nothing Then
MessageBox.Show("Excel is not properly installed!!")
End If
Dim MyFile As String = Dir$(Path + "\" + Filename)
If MyFile = Filename Then
ExcelWorkbook1 = xlApp.Workbooks.Open(Path + "\" + Filename) 'The declaration here is moved outside of the sub, granting access to the other subs
Else
MessageBox.Show("Excel not found!")
End If
End Sub
You have to declare ExcelWorkbook1 as a class property then use it in yours methods:
Property ExcelWorkbook1 As Workbook
Public Sub OpenExcel(ByVal Path As String, ByVal Filename As String)
Dim xlApp As Excel.Application = New Microsoft.Office.Interop.Excel.Application()
xlApp.Visible = True
If xlApp Is Nothing Then
MessageBox.Show("Excel is not properly installed!!")
End If
Dim MyFile As String = Dir$(Path + "\" + Filename)
If MyFile = Filename Then
ExcelWorkbook1 = xlApp.Workbooks.Open(Path + "\" + Filename)
Else
MessageBox.Show("Excel not found!")
End If
End Sub
Public Sub SelectRangeOfCells(ByVal Sheet As String, ByVal Range As String)
ExcelWorkbook1.Worksheets(Sheet).activate
End Sub
More about properties in VB.NET you can learn from MSDN
I am unzipping a file in a folder, and saving in a new location. How do I rename that file immediately after unzipping it? After unzipping I will have a file like 1234_data.csv, how do I rewrite that as whatiwant.csv ?
I am aware I need to use a line such as Name oldfile As NewFileName
Sub Unzip1(str_FILENAME As String)
Dim oApp As Object
Dim Fname As Variant
Dim FnameTrunc As Variant
Dim FnameLength As Long
'Fname = str_FILENAME 'Commented out to show example file name
Fname = "file.zip"
FnameLength = Len(Fname)
If Fname = False Then
'Do nothing
Else
'Extract the files into the newly created folder
Set oApp = CreateObject("Shell.Application")
oApp.NameSpace("C:\Users\Andrew\folder").CopyHere oApp.NameSpace(Fname).Items
DoEvents
End If
End Sub
strPath = “c:\tempzips\”
Fname = "new_file_name.zip"
If Len(Fname) Then
Name strPath + "original.zip" As strPath + Fname
End If
Alternate based on comment
Sub post_unzip(str_just_unzipped_filename As String, str_new_filename As String)
str_path = "c:\thepathtothezips\"
Name strpath + str_just_unzipped_filename As strpath + str_new_filename
End Sub
I am creating an vba-access application with a drop down box Combo_History that gives the user the ability to launch a .pdf file from a sub-folder within a main folder called "Scanned Work Orders (Archives)". What I am trying to do is use a certain number called an "M" number(M number because every number starts with an M ex: M765196) to find this file without using a specific sub folder here is what i have so far:
Dim fso, oFolder, oSubfolder, oFile, queue As Collection
Set fso = CreateObject("Scripting.FileSystemObject")
Set queue = New Collection
queue.Add fso.GetFolder("T:\Scanned Work Orders (Archives)")
Do While queue.Count > 0
Set oFolder = queue(1)
queue.Remove 1 'dequeue
If oFile = Combo_History.Value Then
Application.FollowHyperlink ("T:\Scanned Work Orders (Archives)" & oFile)
End If
For Each oSubfolder In oFolder.SubFolders
queue.Add oSubfolder 'enqueue
Next oSubfolder
For Each oFile In oFolder.Files
If oFile = Combo_History.Value Then
Application.FollowHyperlink ("T:\Scanned Work Orders (Archives)" & oFile)
End If
Next oFile
Loop
The problem is it gets stuck in an infinite loop because it cannot find the .pdf with the keyword name M765196 even though it is in that folder. Is there something im missing? Or an easier way to find the .pdf file?
I'm adding a second answer here because solving for a wildcard differed more than I anticipated from the original.
Searching for files using a wildcard isn't difficult, but it comes with some implications, such as returning a list of results instead of a single result. In addition, I fortunately ran into a permissions error on one of my subfolders which caused me to think about how to handle that situation.
Option Explicit
Private recurseDepth As Integer
Sub test()
Dim rootFolder As String
Dim filename As String
Dim resultFiles() As String
Dim i As Integer
rootFolder = "C:\Temp"
filename = "*.pdf"
If FindFiles(rootFolder, filename, resultFiles) > 0 Then
For i = 1 To UBound(resultFiles)
Debug.Print Format(i, "00") & ": " & resultFiles(i)
Next i
Else
Debug.Print "No files found!"
End If
End Sub
Public Function FindFiles(thisFolder As String, filespec As String, _
ByRef fileList() As String) As Integer
'--- starts in the given folder and checks all files against the filespec.
' the filespec MAY HAVE A WILDCARD specified, so the function returns
' an array of full pathnames (strings) to each file that matches
' Parameters: thisFolder - string containing a full path to the root
' folder for the search
' filespec - string containing a single filename to
' search for, --or--
' string containing a wildcard string of
' files to search for
' (result==>)fileList - an array of strings, each will be a full
' path to a file matching the input filespec
' Returns: (integer) count of the files found that match the filespec
On Error GoTo Error_FindFile
Static fso As Object
Static pathCollection As Collection
Dim fullFilePath As String
Dim oFile As Object
Dim oFolder As Object
Dim oSubfolder As Object
'--- first time through, set up the working objects
If recurseDepth = 0 Then
Set fso = CreateObject("Scripting.FileSystemObject")
Set pathCollection = New Collection
End If
recurseDepth = recurseDepth + 1
'--- focus on the given folder
Set oFolder = fso.GetFolder(thisFolder)
'--- first test if we have permissions to access the folder and
' if there are any files in the folder
On Error Resume Next
If oFolder.Files.Count > 0 Then
If Err.Number = 0 Then
'--- loop through all items in the folder. some are files and
' some are folders -- use recursion to search the subfolders
For Each oFile In oFolder.Files
If oFile.Name Like filespec Then
pathCollection.Add oFolder.Path & "\" & oFile.Name
End If
Next oFile
For Each oSubfolder In oFolder.SubFolders
FindFiles oSubfolder.Path, filespec, fileList
Next oSubfolder
Else
'--- if we get here it's usually a permissions error, so
' just skip this folder
Err.Clear
End If
End If
On Error GoTo Error_FindFile
Exit_FindFile:
recurseDepth = recurseDepth - 1
If (recurseDepth = 0) And (pathCollection.Count > 0) Then
'--- pull the paths out of the collection and make an array, because most
' programs uses arrays more easily
ReDim fileList(1 To pathCollection.Count)
Dim i As Integer
For i = 1 To pathCollection.Count
fileList(i) = pathCollection.Item(i)
Next i
End If
FindFiles = pathCollection.Count
Exit Function
Error_FindFile:
Debug.Print "Error (" & Err.Number & "): " & Err.Description & _
" on " & oSubfolder.Path
GoTo Exit_FindFile
End Function
Your loop setup didn't lend itself very well to recursion in looking for the file. The code below should work for you.
Also, you're using late-binding for your FileSystemObjects - which is perfectly fine. But the way you have them declared causes them all to be evaluated as Variants. It may be a pain, but it's better to break out each variable Dim on as separate line and to exactly specify what type it should be.
Option Explicit
Sub test()
Dim fso As Object
Dim rootFolder As String
Dim filename As String
Dim fullpath As String
Set fso = CreateObject("Scripting.FileSystemObject")
rootFolder = "C:\Users\user\Documents"
filename = "testfile.txt"
fullpath = FindFile(fso, rootFolder, filename)
Debug.Print "file is ";
If Len(fullpath) > 0 Then
Debug.Print "FOUND! : " & fullpath
Else
Debug.Print "NOT found. Go look for it yourself!"
End If
End Sub
Function FindFile(fso As Object, thisFolder As String, filename As String) As String
On Error GoTo Error_FindFile
Dim fullFilePath As String
Dim oFolder As Object
Dim oSubfolder As Object
Set oFolder = fso.GetFolder(thisFolder)
'--- first check if the file is in the current folder
fullFilePath = oFolder.Path & "\" & filename
If fso.FileExists(fullFilePath) Then
'--- we're done, nothing more to do here
Else
'--- the file isn't in this folder, so check for any subfolders and search there
fullFilePath = ""
For Each oSubfolder In oFolder.SubFolders
Debug.Print "looking in " & oSubfolder.Path
If FindFile(fso, oSubfolder.Path, filename) <> "" Then
'--- found the file, so return the full path
fullFilePath = oSubfolder.Path & "\" & filename
Exit For
End If
Next oSubfolder
End If
Exit_FindFile:
FindFile = fullFilePath
Exit Function
Error_FindFile:
'--- we'll probably get mostly permission errors, so just skip (or log, or print out)
' the permission error and move on
If Err.Number = 70 Then
Debug.Print "Permission error on " & oSubfolder.Path
End If
GoTo Exit_FindFile
End Function
This page suggests the following technique for finding a wildcard recursively:
Sub Macro1()
Dim colFiles As New Collection
RecursiveDir colFiles, "C:\Photos\", "*.jpg", True
Dim vFile As Variant
For Each vFile In colFiles
Debug.Print vFile
Next vFile
End Sub
Public Function RecursiveDir(colFiles As Collection, _
strFolder As String, _
strFileSpec As String, _
bIncludeSubfolders As Boolean)
Dim strTemp As String
Dim colFolders As New Collection
Dim vFolderName As Variant
'Add files in strFolder matching strFileSpec to colFiles
strFolder = TrailingSlash(strFolder)
strTemp = Dir(strFolder & strFileSpec)
Do While strTemp <> vbNullString
colFiles.Add strFolder & strTemp
strTemp = Dir
Loop
If bIncludeSubfolders Then
'Fill colFolders with list of subdirectories of strFolder
strTemp = Dir(strFolder, vbDirectory)
Do While strTemp <> vbNullString
If (strTemp <> ".") And (strTemp <> "..") Then
If (GetAttr(strFolder & strTemp) And vbDirectory) <> 0 Then
colFolders.Add strTemp
End If
End If
strTemp = Dir
Loop
'Call RecursiveDir for each subfolder in colFolders
For Each vFolderName In colFolders
Call RecursiveDir(colFiles, strFolder & vFolderName, strFileSpec, True)
Next vFolderName
End If
End Function
Public Function TrailingSlash(strFolder As String) As String
If Len(strFolder) > 0 Then
If Right(strFolder, 1) = "\" Then
TrailingSlash = strFolder
Else
TrailingSlash = strFolder & "\"
End If
End If
End Function
I'd like to contribute to PeterT's solution (second answer)! It appears I don't have enough points to comment, so I'm posting this as an answer.
I tested the solution and it works, but it has some (minor) bugs! I didn't test it on a server with complicated privileges, but I'll eventually have to do that in the near future!
If the startFolder is empty (no files, but subfolders) the function doesn't continue to search in startFolders' subfolders.
Search for A*.pdf and a*.PDF will not give the same result. Given the fact that Windows file system is case insensitive, it's wise to have case insensitive search. Perhaps it won't work on MAC?!
In addition, I added two (optional) extra parameters, code for garbage collection and early binding for FSO objects (I prefer that!):
boolean subFolders - if false the function will not search beyond the
startFolder
boolean fullPath - if false the function will return only file names without the path; useful (at least to me) especially if subFolders=false.
After the search finishes (recurseDepth = 0) all objects are set to Nothing.
Here is the code:
Public Function FindFiles( _
ByVal startFolder As String, _
ByVal fileSpec As String, _
ByRef fileList() As String, _
Optional ByVal subFolders As Boolean = True, _
Optional ByVal fullPath As Boolean = True) _
As Long
'--- starts in the given folder and checks all files against the filespec.
' the filespec MAY HAVE A WILDCARD specified, so the function returns
' an array of files with or withour full pathnames (strings) to each file that matches
' Parameters: startFolder - string containing a full path to the root
' folder for the search
' fileSpec - string containing a single filename to
' search for, --or--
' string containing a wildcard string of
' files to search for
' (result==>)fileList - an array of strings, each will be a full
' path to a file matching the input filespec
' subFolders - include subfolders in startFolder
' fullPath - true=>fullFile path; false=>fileName only
' Returns: (integer) count of the files found that match the filespec
Dim fullFilePath As String
Dim Path As String
Static fso As FileSystemObject
Static pathCollection As Collection
Dim oFile As file
Dim oFolder As Folder
Dim oSubfolder As Folder
On Error GoTo Error_FindFile
'--- first time through, set up the working objects
If recurseDepth = 0 Then
Set fso = New FileSystemObject ' CreateObject("Scripting.FileSystemObject")
Set pathCollection = New Collection
End If
recurseDepth = recurseDepth + 1
'--- focus on the given folder
Set oFolder = fso.GetFolder(startFolder)
'--- first test if we have permissions to access the folder and
' if there are any files in the folder
On Error Resume Next
If oFolder.files.Count > 0 Or oFolder.subFolders.Count > 0 Then
If Err.Number = 0 Then
'--- loop through all items in the folder. some are files and
' some are folders -- use recursion to search the subfolders
If fullPath Then
Path = oFolder.Path & "\"
Else
Path = ""
End If
For Each oFile In oFolder.files
' If oFile.name Like fileSpec Then
If LCase(oFile.name) Like LCase(fileSpec) Then
pathCollection.Add Path & oFile.name
End If
Next oFile
If subFolders Then
For Each oSubfolder In oFolder.subFolders
FindFiles oSubfolder.Path, fileSpec, fileList, subFolders, fullPath
Next oSubfolder
End If
Else
'--- if we get here it's usually a permissions error, so
' just skip this folder
Err.Clear
End If
End If
On Error GoTo Error_FindFile
Exit_FindFile:
recurseDepth = recurseDepth - 1
If (recurseDepth = 0) Then
If (pathCollection.Count > 0) Then
'--- pull the paths out of the collection and make an array, because most
' programs uses arrays more easily
ReDim fileList(1 To pathCollection.Count)
Dim i As Integer
For i = 1 To pathCollection.Count
fileList(i) = pathCollection.Item(i)
Next i
End If
FindFiles = pathCollection.Count
Set fso = Nothing
Set pathCollection = Nothing
Set oFile = Nothing
Set oFolder = Nothing
Set oSubfolder = Nothing
End If
Exit Function
Error_FindFile:
Debug.Print "Error (" & Err.Number & "): " & Err.Description & _
" on " & oSubfolder.Path
GoTo Exit_FindFile
End Function
I have a script that goes and grabs all of the documents from a certain folder and lists all of the files in that folder. It then goes and makes a link to open these files from inside of Excel. I was wondering if there was a way to put it in a shell so that the files only opened in notepad. The code that I am using right now is:
Sub MakeLink(ByVal cell As Range, ByVal url As String, ByVal txt As String, ByVal tooltip_text As String)
ActiveSheet.Hyperlinks.Add _
Anchor:=cell, _
Address:=url, _
ScreenTip:=tooltip_text, _
TextToDisplay:=txt
End Sub
Sub Portfolios()
Range("A1:Z200").Clear
Dim objFSO As Object
Dim objFolder As Object
Dim objFile As Object
Dim ws As Worksheet
Range("A3").Font.Bold = True
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set ws = Worksheets("Library")
'Get the folder object associated with the directory
Set objFolder = objFSO.GetFolder("C:\Portfolios")
ws.Cells(3, 1).Value = "The files found in " & objFolder.Name & " are:"
'Loop through the Files collection
For Each objFile In objFolder.Files
'ws.Cells(ws.UsedRange.Rows.Count + 3, 2).Value = objFile.Name
MakeLink ws.Cells(ws.UsedRange.Rows.Count + 3, 2), objFile, objFile.Name, objFile.Name
Next
'Clean up!
Set objFolder = Nothing
Set objFile = Nothing
Set objFSO = Nothing
End Sub
I know I have to do something along the lines of MakeLink = Shell("C:\WINDOWS\notepad.exe", 1) but I seem to be hitting a bit of a snag as to where this will fit.
Thanks,
F
Files will open in whatever is the default program for the file type.
If you want to force them to open in notepad then you'll have to write some code to process the Worksheet_FollowHyperlink event: you can get the cell text from the Target parameter and shell out notepad from there.
To prevent problems with the hyperlink taking users elsewhere, just set the target address to the same cell as the one containing the hyperlink.
Private Sub Worksheet_FollowHyperlink(ByVal Target As Hyperlink)
Dim fPath As String, res
fPath = Target.TextToDisplay
res = Shell("notepad.exe """ & fPath & """", vbNormalFocus)
End Sub
To create the hyperlinks:
Sub MakeLink(rng As Range, txt As String)
Dim addr As String
addr = "'" & rng.Parent.Name & "'!" & rng.Address(False, False)
rng.Parent.Hyperlinks.add Anchor:=rng, Address:="", _
SubAddress:=addr, TextToDisplay:=txt
End Sub