I was looking for a way to move the 5 oldest modified files in a folder to a different folder.
I came across some helpful pieces of code and I revised it to this:
Dim files
Dim startFolder
Dim destinationFolder
Dim oldestFile
Dim file
Dim FSO
startFolder = "C:\logs\current"
destinationFolder = "C:\logs\backup"
Set FSO = CreateObject("Scripting.FileSystemObject")
Set files = FSO.GetFolder(StartFolder).files
Set oldFiles = CreateObject("System.Collections.ArrayList")
If files.Count <= 5 Then
WScript.Quit
End If
For i = 0 To 4
Set files = FSO.GetFolder(StartFolder).files
Set oldFiles = Nothing
For Each file In files
If Not IsObject(oldestFile) Then
Set oldestFile = file
Else
If file.DateLastModified < oldestFile.DateLastModified Then
Set oldestFile = file
End If
End If
Next
WScript.Echo "OLDEST: " & oldestFile.Name
oldestFile.Move destinationFolder & "\" & oldestFile.Name
Next
Basically what it supposed to do is:
loop 5 times,
each time loop through the files and assign the oldest to oldestFile,
move the file to a different location.
However, it doesn't work, it's echoing the first file's name 5 times and move just this one.
I thought I should set the objects to Nothing to start fresh but to no avail.
You need to reset the variable oldestFile at the beginning (or end) of your loop, not the variable oldFiles.
For i = 0 To 4
Set files = FSO.GetFolder(StartFolder).files
Set oldestFile = Nothing
For Each file In files
...
Next
WScript.Echo "OLDEST: " & oldestFile.Name
oldestFile.Move destinationFolder & "\" & oldestFile.Name
Next
Otherwise the value of oldestFile will never change, because even after being moved the referenced file ramains the oldest file compared to the files in the source folder.
Related
I have a script to list the files in a directory including their length:
Set WshShell = WScript.CreateObject("WScript.Shell")
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set txtOut = objFSO.CreateTextFile("d:\FileNameLength.csv")
FoldersRec "d:\MyData"
Sub FoldersRec(Startfolder)
Set objFolder = objFSO.GetFolder(Startfolder)
Set colSubfolders = objFolder.Subfolders
For Each objSubfolder In colSubfolders
FoldersRec(objSubfolder)
Next
For Each file In objFolder.Files
strZeile = file & ";" & Len(file)
txtOut.WriteLine strZeile
Next
''MsgBox "Done"
End Sub
It works fine. What I would like to change is to convert it to an explicit loop after which I can add something, like for instance a message box.
If I just enter MsgBox "Done" just before End Sub it would appear for each file.
Therefore, the idea is to add something like loop while file exists or loop while Len(file)>0. But I am not quite sure about the syntax.
Why not like this:
Set txtOut = objFSO.CreateTextFile("d:\FileNameLength.csv")
FoldersRec "d:\MyData"
MsgBox "Done"
I would rewrite your code like this:
Set fso = CreateObject("Scripting.FileSystemObject")
ListSizes fso.GetFolder("D:\MyData"), fso.CreateTextFile("d:\FileNameLength.csv")
MsgBox "Done"
Sub ListSizes(folder, outStream)
Dim subfolder, file
For Each subfolder In folder.Subfolders
ListSizes subfolder, outStream
Next
For Each file In folder.Files
outStream.WriteLine file & ";" & file.Size
Next
End Sub
This way you can pass any Stream object to ListSizes, for example the console (when called through cscript.exe):
ListSizes fso.GetFolder("D:\MyData"), WScript.StdOut
Also, I suspect want the file .Size (in bytes), and not the length of the file path?
So, here is the best solution for my problem:
Set fso = CreateObject("Scripting.FileSystemObject")
ListSizes fso.GetFolder("s:"), fso.CreateTextFile("r:\FileNameLength.csv")
MsgBox "Done"
Sub ListSizes(folder, outStream)
Dim subfolder, file
For Each subfolder In folder.Subfolders
On Error Resume Next
ListSizes subfolder, outStream
Next
For Each file In folder.Files
outStream.WriteLine file & ";" & file.Size & ";" & len(file)
Next
End Sub
With this solution I get both file name length and file size.
I try to open all *.xlsx files in a specified folder and store the filehandles in an array.
My code looks like this
Dim Files() As Workbook
ReDim Files(Count)
File = Dir(Path & "\*.xlsx")
Count = 0
Do While File <> ""
Set Files(Count) = Workbooks.Open(Path & File, , True)
Count = Count + 1
File = Dir()
Loop
The code seems to work, however, when I run it a second time (hitting the run button again), I get an error number 13.
Debugging the code I tracked the problem to the line
Set Files(Count) = Workbooks.Open(Path & File, , True)
As I am unexperienced with vba I guess I didn't do this the right way...
What would be a preferable way to store filehandles to all files in a specific folder in an array?
you're missing a path separator
Set Files(Count) = Workbooks.Open(Path & "\" & File, , True)
The code should be:
Dim Files() As Workbook
Dim Count As Integer
ReDim Files(Count)
File = Dir(Path & "\*.xlsx")
Count = 0
Do While File <> ""
ReDim Preserve Files(Count)
Set Files(Count) = Workbooks.Open(Path & File, , True)
Count = Count + 1
File = Dir()
Loop
You need to redim your array. Preserve keeps the existing data.
Hiya Guys I have a text file with locations of files I'm looking for a way to read the text file and then use those locations as a source location and copy the files to a seperate destination.
I've been playing around and have seen about dynamic arrays but cant seem to understand how to put the contents of the array into variables to read as source location.
example of what I have done so far
Dim TxtFile
dim strDestinationFolder
strDestinationFolder = "\\SERVER\DESTLOGS"
TxtFile = "c:\windows\temp\SOFTWARELOG.txt"
Dim fso: Set fso = CreateObject("Scripting.FileSystemObject")
Dim f: Set f = fso.OpenTextFile(TxtFile)
Do Until f.AtEndOfStream
WScript.Echo "PSTLocation: " & f.ReadLine ; I can read each line here in the txt file
fso.CopyFile strDestinationFolder, f.REadline
Loop
I've also tried playing with, but not sure where to start though it looks the most reliable?
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objTextFile = objFSO.OpenTextFile _
(TxtFile, ForReading)
Do Until objTextFile.AtEndOfStream
strNextLine = objTextFile.Readline
arrServiceList = Split(strNextLine , ",")
WScript.Echo "Server: " & arrServiceList(0)
WScript.Echo "Service: " & objTextFile
For k = 1 to UBound(arrServiceList)
WScript.Echo vbTab & "Service: " & arrServiceList(i)
Next
Loop
Any Guidance please as to what is the best way I should go about this with vbs.
Thanks
WScript.Echo "PSTLocation: " & f.ReadLine
fso.CopyFile strDestinationFolder, f.REadline
Your code echoes one line read from the file, and then tries to copy the destination folder to the next line read from the file.
If you want to do more than one thing with a line read from a file you need to assign the read line to a variable and then use that variable. Also, you need to switch the arguments of the CopyFile method. Source comes first, then destination. Plus, if you want the destination to be a folder, it needs a trailing backslash (otherwise you'd try to overwrite a folder with a file, which raises an error).
Do Until f.AtEndOfStream
line = Trim(f.ReadLine)
WScript.Echo "PSTLocation: " & line
If fso.FileExists(line) Then fso.CopyFile line, strDestinationFolder & "\"
Loop
The Trim() accounts for spurious leading/trailing spaces in the read line, and it's always a good idea to check if a file actually exists before you try to do anything with it.
Edit: For detecting an existing destination file and appending a running number to the file name try something like this:
basename = fso.GetBaseName(line)
extension = fso.GetExtensionName(line)
destinationFile = fso.BuildPath(strDestinationFolder, basename & "." & extension)
i = 1
Do While fso.FileExists(destinationFile)
filename = basename & i & "." & extension
destinationFile = fso.BuildPath(strDestinationFolder, filename)
i = i + 1
Loop
fso.CopyFile line, destinationFile
I have a system that's running an old FoxPro program which generates 8 character long DBF files. We make a back up of the program folder each day, but at 5pm the program has generated so many of these garbage dbf's that it's a nuisance. I would just set a del *.dbf in the back up script but there are a few dbf with letters in their name that are needed to run the program.
Files are located in F:\Clean This\
Any numerically titled .dbf files need to be deleted
Any alphabetically titled .dbf file should be left alone
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = "F:\Clean This\"
Set objFolder = objFSO.GetFolder(objStartFolder)
Set colFiles = objFolder.Files
collide = "ABCEDFGHIJKLMNOPQRSTUVWXYZ"
For Each objFile in colFiles
If UCase(objFSO.GetExtensionName(objFile.name)) = "DBF" Then
num = 1
For num = 1 to 26 'find files with names start with # 0-9
If Left(objFile.Name,1) = Left(collide,num) Then
Wscript.Echo "Save " & objFile.Name
Else If int(Left(objFile.Name,1)) > 0 Then
Wscript.Echo "Delete!"
End IF
End If
Next
End If
Next
As you can tell the If statements can be done better, I'm unsure how to better work it out. The two Wscript.Echo commands are just placeholders because if anything else I can't get find a suitable delete function that would work in a dos environment (I've already tried kill, no).
Suggestions and improvements would be much appreciated!
Use IsNumeric() to check for file names consisting of digits only:
>> For Each sN In Split("abc 123 1O1 101")
>> If IsNumeric(sN) Then
>> WScript.Echo "delete", sN
>> Else
>> WScript.Echo "keep", sN
>> End If
>> Next
>>
keep abc
delete 123
keep 1O1
delete 101
Your check fails, because you use Left() where you should use Mid():
>> collide = "ABCEDFGHIJKLMNOPQRSTUVWXYZ"
>> num = 5
>> WScript.Echo Left(collide,num)
>> WScript.Echo Mid(collide,num,1)
>>
ABCED
D
and even then Left(objFile.Name,1) will look at only the first character of the file name.
Update (wrt comments):
Apply IsNumeric() to the base name:
Dim oFile
For Each oFile In goFS.GetFolder("..\testdata\17817161").Files
WScript.Stdout.Write oFile.Name
If "dbf" = LCase(goFS.GetExtensionName(oFile.Name)) Then
If IsNumeric(goFS.GetBaseName(oFile.Name)) Then
WScript.Stdout.WriteLine " delete"
Else
WScript.Stdout.WriteLine " keep"
End If
Else
WScript.Stdout.WriteLine " ignore"
End If
Next
output:
123.dbf delete
123.txt ignore
abc.dbf keep
I did finally got it working, and here's what the end result was. It works wonderfully and the higher ups and others were rather impress that it worked better than the built in utility of the program we're backing up. Score!
Set objFSO = CreateObject("Scripting.FileSystemObject")
objStartFolder = "..\System Folder"
Set objFolder = objFSO.GetFolder(objStartFolder)
Set DirFiles = objFolder.Files
Dim oFile
For Each objFile in DirFiles
If "dbf" = LCase(objFSO.GetExtensionName(objFile.Name)) Then
If IsNumeric(objFSO.GetBaseName(objFile.Name)) Then
objFSO.DeleteFile(objFile)
End If
End If
Next
I have a VBS file that I am trying to use to determine what folders and files are in a certain directory. I believe I have the code written correctly, but whenever I try to write out the file or current directory I get a blank text document with nothing but the root directory written out. Any advice would be greatly appreciated.
Dim NewFile
Function GetFolders (strFolderPath)
Dim objCurrentFolder, colSubfolders, objFolder, files
Set objCurrentFolder = objFSO.GetFolder(strFolderPath)
Set colSubfolders = objCurrentFolder.SubFolders
For Each objFolder In colSubfolders
NewFile.WriteLine(" - " & objFolder.Path)
Set files = folder.Files
For each folderIdx In files
NewFile.WriteLine(" - "& folderIdx.Name)
Next
Call GetFolders (objFolder.Path)
Next
End Function
Dim fso, sFolder
Set fso = CreateObject("Scripting.FileSystemObject")
sFolder = Wscript.Arguments.Item(0)
If sFolder = "" Then
Wscript.Echo "No Folder parameter was passed"
Wscript.Quit
End If
Set NewFile = fso.CreateTextFile(sFolder&"\FileList.txt", True)
NewFile.WriteLine(sFolder)
Call GetFolders(sFolder)
NewFile.Close
You haven't payed sufficient attention to your variable naming. Your script is a good example of the reason why all VBScripts should start with the line:-
Option Explicit
This would highlight all the variables that haven't been declared which in turn will point out typos and inconsistencies in variable naming. Here is how I would write it:-
Option Explicit
Dim msFolder : msFolder = Wscript.Arguments.Item(0)
If msFolder = "" Then
Wscript.Echo "No Folder parameter was passed"
Wscript.Quit
End If
Dim mfso : Set mfso = CreateObject("Scripting.FileSystemObject")
Dim moTextStream : Set moTextStream = mfso.CreateTextFile(msFolder & "\FileList.txt", True)
moTextStream.WriteLine(msFolder)
WriteFolders mfso.GetFolder(msFolder)
moTextStream.Close
Sub WriteFolders(oParentFolder)
Dim oFolder
For Each oFolder In oParentFolder.SubFolders
moTextStream.WriteLine(" - " & oFolder.Path)
Dim oFile
For Each oFile In oFolder.Files
moTextStream.WriteLine(" - " & oFile.Name)
Next
WriteFolders oFolder
Next
End Sub