VBscript to export total row count to another csv with the ability to add a custom name - vbscript

I am using the following VBscript to get the total row count from my csv files. I need help in exporting the returned line count to a csv which will have two columns Name and Count name will be anything and the count is the returned count.
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objArgs = WScript.Arguments
myFile = objArgs(0)
Set objFile = objFSO.OpenTextFile(myFile,1)
Do Until objFile.AtEndOfLine
line = objFile.Line
objFile.ReadLine
Loop
WScript.Echo "Line count of", myFile , "is", line
The way i would like to call the script would be:
Cscript 'vbscript_name' file_name_to_count 'custom_name' 'export_count.csv'
Thanks

Maybe I not see where is the break, as you only need to create new file and write just 2 lines in, but it w'd be something like this:
Set objFile = objFSO.OpenTextFile(objArgs(2), 2, True)
objFile.WriteLine "Name,Count"
objFile.WriteLine objArgs(1) & "," & line
objFile.Close
And just to become more friendly, here is the whole deal:
Set objArgs = WScript.Arguments
iLinesCount = FileLinesCount(objArgs(0))
DumpResult objArgs(2), objArgs(1), iLinesCount
WScript.Echo "File: " & objArgs(0) & " has " & iLinesCount & " lines"
Function FileLinesCount(strFileName)
With CreateObject("Scripting.FileSystemObject")
With .OpenTextFile(strFileName, 1)
Do Until .AtEndOfStream
Call .ReadLine
Loop
FileLinesCount = .Line
End With
End With
End Function
Sub DumpResult(strFileName, strCustomName, iCount)
With CreateObject("Scripting.FileSystemObject")
With .OpenTextFile(strFileName, 2, True)
.WriteLine "Name,Count"
.WriteLine strCustomName & "," & iCount
End With
End With
End Sub
Also it's good to add error checks for your command line arguments, but I live this simple task to you, cheers!
P.S. I suppose you'll prefer to append your count data to existing file instead of creating new file for each counted source file. If so, you have a very little work on DumpResult function, just need to open the file for appending (ForAppending = 8) and add "header" (column names) only then needs (i.e. when the file is newly created):
' modified version w`d be:
Sub DumpResult(strFileName, strCustomName, iCount)
With CreateObject("Scripting.FileSystemObject")
With .OpenTextFile(strFileName, 8, True)
If .Line = 1 Then ' new empty file
.WriteLine "Name,Count" ' add column names
End If
.WriteLine strCustomName & "," & iCount
End With
End With
End Sub

Related

Merging last slides from multiple PowerPoint Presentations [duplicate]

We have some set of (powerpoint) pptx files which are targeted for different audiences
I was thinking of merging different slide sets based on target audiance
I want to know if it's possible to
Merge multiple powerpoint files into one
If a single file is changed, i want the same change to reflect into the merged file
Is this possible graphically or by a VBA?
Ex:
A.pptx
B.pptx
C.pptx
D.pptx
E.pptx
Set 1 (Dev.pptx):
A.pptx
B.pptx
D.pptx
Set 2 (Manager.pptx)
A.pptx
D.pptx
E.pptx
Set 3 (all.pptx)
A.pptx
B.pptx
C.pptx
D.pptx
E.pptx
If i change any one of the Pptx (A,b,c,d,e) the combined files should be updated automatically
The simplest and probably most reliable solution would be to put all of the slides into one file and then create custom shows, one for each target audience.
Another approach would be to have a main "menu" presentation, one slide with links to sub-presentations, one per audience. Each of those presentations would have its own "menu" slide that links to A.pptx, B.pptx etc. as needed.
At the end of A.pptx, add an End Presentation link; click on that (or just press ESC to quit the presentation) and you'll be returned to the sub-menu presentation.
This is possible with VBS on a Windows with installed PowerPoint.exe.
Create a script named merge.vbs with this content:
Option Explicit
Sub WriteLine ( strLine )
WScript.Stdout.WriteLine strLine
End Sub
Sub WriteError ( strLine )
WScript.Stderr.WriteLine strLine
End Sub
Dim inputFile1
Dim inputFile2
Dim outputFile
Dim objPPT
Dim objFso
Dim objPresentation
If WScript.Arguments.Count <> 3 Then
WriteError "You need to specify 2 input files and one output file."
WScript.Quit 1
End If
inputFile1 = WScript.Arguments(0)
inputFile2 = WScript.Arguments(1)
outputFile = WScript.Arguments(2)
Set objFso = CreateObject("Scripting.FileSystemObject")
If Not objFso.FileExists( inputFile1 ) Then
WriteError "Unable to find your input file " & inputFile1
WScript.Quit 1
End If
If Not objFso.FileExists( inputFile2 ) Then
WriteError "Unable to find your input file " & inputFile2
WScript.Quit 1
End If
WriteLine "Input File 1 : " & inputFile1
WriteLine "Input File 2 : " & inputFile2
WriteLine "Output File: " & outputFile
Set objPPT = CreateObject( "PowerPoint.Application" )
' Open presentation with window hidden
Set objPresentation = objPPT.Presentations.Open(inputFile1, True, False, False)
mergeAndKeepSourceFormatting objPresentation, inputFile2
' Reference for this at https://learn.microsoft.com/en-us/office/vba/api/powerpoint.presentation.saveas
WriteLine "Saving File: " & outputFile
objPresentation.SaveAs outputFile
objPresentation.Close
ObjPPT.Quit
'
' Add the file to the loaded presentation
'
Sub mergeAndKeepSourceFormatting(ByRef objPresentation, ByVal newPptxFile)
WriteLine "Merging file: " & newPptxFile
Dim newSlides
Dim oldSlides
oldSlides = objPresentation.Slides.Count
newSlides = objPresentation.Slides.InsertFromFile( newPptxFile, objPresentation.Slides.Count)
objPresentation.Slides.Range(FillRangeArray(oldSlides + 1, oldSlides + newSlides)).ApplyTemplate newPptxFile
End Sub
Function FillRangeArray(n1, n2)
Dim myArr()
Redim myArr(n2 - n1)
Dim i
For i = 0 to (n2 - n1)
myArr(i) = n1 + i
Next
FillRangeArray = myArr
End Function
Then from the command line you can call it:
CSCRIPT merge.vbs "A.pptx" "B.pptx" "resultA_B.pptx"
Please adjust the script to your needs or call it several times to merge the resulting file with the next one.

How to append text from one file to another file after a specific line using VBScript?

I need to insert the contents of a text file into another existing text file after the line with a specific word in it.
Here is my code.
'//OPEN FILE and READ
Set objFileToRead = fso.OpenTextFile(ActiveDocument.Path & "\file.txt", 1)
strFileText = objFileToRead.ReadAll()
objFileToRead.Close
objStartFolder = ActiveDocument.Path
Set objFolder = fso.GetFolder(objStartFolder)
Set colFiles = objFolder.files
For Each objFile In colFiles
If fso.GetExtensionName(objFile.Name) = "opf" Then
filename = objFile.Name
End If
Next
MsgBox filename
'///PASTE
If fso.FileExists(ActiveDocument.Path & "\" & filename) Then
MsgBox filename
Set objFile = fso.OpenTextFile(ActiveDocument.Path & "\" & filename)
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
If InStr(strLine, "<manifest>") = 1 Then
MsgBox filename
objFile.WriteLine vbCrLf & strFileText
objFile.Close
End If
Loop
End If
I get a bad file error in the following line
objFile.WriteLine vbCrLf & strFileText
Can anyone please tell me what is wrong and what I have to do?
You can't write to a file that was opened for reading.
Either write the modified content to a temporary file and replace the original file with it afterwards:
p = fso.BuildPathActiveDocument.Path, filename)
Set f1 = fso.OpenTextFile(p)
Set f2 = fso.OpenTextFile(p & ".tmp", 2, True)
Do Until f1.AtEndOfStream
line = f1.ReadLine
f2.WriteLine line
If InStr(line, "<manifest>") = 1 Then f2.WriteLine strFileText
Loop
f1.Close
f2.Close
fso.DeleteFile p, True
fso.GetFile(p & ".tmp").Name = filename
or read the entire content into memory before writing the modified content back to the original file:
p = fso.BuildPathActiveDocument.Path, filename)
txt = Split(fso.OpenTextFile(p).ReadAll, vbNewLine)
Set f = fso.OpenTextFile(p, 2)
For Each line In original
f.WriteLine line
If InStr(line, "<manifest>") = 1 Then f.WriteLine strFileText
Next
f.Close
Note that the latter shouldn't be used for large files, lest your computer come grinding to a halt due to memory exhaustion.

replacing XML values in For-Each and returning in function

The script I have here is attempting to do recurse through an XML file, storing each RegEx match (stored in a search array) into 2 result arrays; 1 for start date, 1 for end date.
Ubounds of both arrays are checked for equality then the text is passed to a function that uses XMLDOM to find the End_Date node in each parent node, then passes that text to another function, adding 30 days and then passing it back, replacing the previous value. Then it's supposed to write back the contents to the file and save it.
I've got a few problems here. 1. I can't get the +30 day value to be passed back to anything past the first parent node--memory space seems to retain the +30 day value from previous For-Each iteration. 2. I can't write anything back to the file.
I was initially writing for text files, but the format changed to XML as the requirements changed on our project.
I'd love to be able to do this all in XMLDOM in vbscript and just use functions to do specific data changes. But my main concern is my sloppy script not doing the basics.
Can anyone help me by pointing out the flaws in the loops I'm running? I've hit a wall and just can't seem to make any more progress!
Here's the XML file I'm reading(shortened to 2 Ad nodes w/ a ton of child nodes removed):
<?xml version="1.0" encoding="utf-8"?>
<XMLFeederRoot>
<ADS_CREATE_TIME>2016-06-07T01:35:39</ADS_CREATE_TIME>
<Ad>
<Ad_Number>d00524224</Ad_Number>
<Start_Date>2016-08-20T00:00:00</Start_Date>
<End_Date>2016-08-20T00:00:00</End_Date>
<Status>Run</Status>
</Ad><Ad>
<Ad_Number>d00524225</Ad_Number>
<Start_Date>2016-08-20T00:00:00</Start_Date>
<End_Date>2016-08-20T00:00:00</End_Date>
<Status>Run</Status>
</Ad>
</XMLFeederRoot>
Here's the script:
'Setting the Regular Expression object and setting occurrences to all in strings searched.
Set objRegEx= CreateObject("VBScript.RegExp")
objRegEx.Global= True
set Shell= createobject("wscript.shell")
Dim FSO, FLD, FIL, TS, strDate, strEDat, i, d, c
Dim strFolder, strContent, strPath
Const ForReading= 1, ForWriting= 2
strFolder= "C:\Scripts\Run"
Set FSO= CreateObject("Scripting.FileSystemObject")
'Get a reference to the folder you want to search
set FLD= FSO.GetFolder(strFolder)
'loop through the folder and get the files
For Each Fil In FLD.Files
'Open the file to read
Set TS= FSO.OpenTextFile(fil.Path, ForReading)
'Read the contents into a variable
strContent= TS.ReadAll
'Close the file
TS.Close
reDim arrMR(1,1)
arrMR(0,0)= "(\s+)(<Start_Date>(.*?)<\/Start_Date>)"
arrMR(1,0)= "(\s+)(<End_Date>(.*?)<\/End_Date>)"
For i= 0 to Ubound(arrMR)
objRegEx.Pattern= arrMR(i,0)
Set objMatches= objRegEx.Execute(strContent)
d=0
For Each objMatch in objMatches
If i= 0 Then
If d>0 Then
reDim Preserve arrStart(d)
Else
reDim arrStart(d)
End If
arrStart(d)= objMatches.Item(d).SubMatches(2)
'Wscript.Echo arrStart(d)
ElseIf i<> 0 Then
If d>0 Then
reDim Preserve arrEnd(d)
ReDim Preserve arrMatch1(d)
Else
reDim arrEnd(d)
ReDim arrMatch1(d)
End If
arrEnd(d)= objMatches.Item(d).SubMatches(2)
arrMatch1(d)= objMatches.Item(d).SubMatches(1)
End If
If objRegEx.Pattern<> arrMR(0,0) Then
If (ubound(arrStart)= ubound(arrEnd)) Then
'Wscript.Echo "Ubounds Match"
Parse strContent
strContent= Parse(strContent)
Else
'Wscript.Echo "Start & End Dates do not match"
End If
End If
d= d+ 1 'increment to next match
Next
Next
'Close the file
TS.Close
'Open the file to overwrite the contents
Set TS= FSO.OpenTextFile(fil.Path, ForWriting)
'Write the contents back
TS.Write strContent
'Close the current file
TS.Close
Next
'Clean up
Set TS= Nothing
Set FLD= Nothing
Set FSO= Nothing
Function Parse(ParseContent)
'Dim sFSpec : sFSpec = FSO.GetAbsolutePathName("C:\Users\j.levine\Desktop\XML Feeder Scripts\Test_Files\monvid.txt")
Dim oXML : Set oXML = CreateObject("Msxml2.DOMDocument.6.0")
Dim strXMLSDat, strXMLarrStartD, XMLEDat
oXML.setProperty "SelectionLanguage", "XPath"
oXML.async = False
oXML.loadXML(ParseContent)
If 0 = oXML.parseError Then
Dim sXPath3 : sXPath3 = "//XMLFeederRoot/Ad[End_Date=Start_Date]"
Dim ndlFnd : Set ndlFnd = oXML.selectNodes(sXPath3)
If 0 = ndlFnd.length Then
WScript.Echo sXPath, "not found"
ElseIf 0<> ndlFnd.length Then
'WScript.Echo "found", ndlFnd.length, "nodes for", sXPath
Dim ndCur, oldNode
For Each ndCur In ndlFnd
oldNode = oXML.selectsinglenode("//End_Date").text
oldNode= XMLSplitArray(oldNode) 'Pass current Date into Array and add 30 days & return as node text
Set newNode= oXML.selectSingleNode("//End_Date")
newNode.text= oldNode
WScript.Echo ndCur.xml
Next
'WScript.Echo "We have nothing to replace"
End If
Else
WScript.Echo oXML.parseError.reason
End If
Parse= ParseContent
End Function
Function XMLSplitArray(strval1)
dim XmlSA, XmlSA2, XMLEDat
XmlSA = split(strval1, "-")
XmlSA(2) = Left(XmlSA(2), 2)
strXMLDate = XmlSA(1) & "/" & XmlSA(2) & "/" & XmlSA(0)
strXMLDate30 = DateAdd("d", 30, strXMLDate)
XmlSA2 = split(strXMLDate30, "/")
'Add zero to the left
XmlSA2(0)= Right("0" & XmlSA2(0), 2)
XmlSA2(1)= Right("0" & XmlSA2(1), 2)
XmlSA2(1) = XmlSA2(1) & "T00:00:00"
XMLEDat = XmlSA2(2) & "-" & XmlSA2(0) & "-" & XmlSA2(1)
XMLSplitArray= XMLEDat
End Function
Thomas was right w/ the KISS method. I took a big step back, and started over.
Here's what I've come up with. It does what I need regarding the Date+30 and writing back to a file. I think this method is cleaner and will allow me to run my other text massaging through functions.
My questions about this new script are:
1. can this be done without having to write to a new file? Keeping to 1 file is easier.
2. Can I avoid cloning the node and deleting the original one and directly change the original node's value?
3. I seem to be missing how to get that last node <Status> onto it's own line.
Script:
Dim xmlDoc: Set xmlDoc = CreateObject("Msxml2.DOMDocument")
xmlDoc.Async = False
xmlDoc.load "C:\Scripts\Run\MonVid-SHORT.xml"
Dim xmldoc2: set xmldoc2 = CreateObject("Msxml2.DOMDocument")
Dim strSkeleton : strSkeleton= "<?xml version=""1.0"" encoding=""utf-8""?>" & _
"<XMLFeederRoot>" & _
"</XMLFeederRoot>"
xmldoc2.loadXML(strSkeleton)
xmldoc2.save "C:\Scripts\Copy\New_MonVid-Short.xml"
xmlDoc2.async = False
xmlDoc2.load "C:\Scripts\Copy\New_MonVid-Short.xml"
Dim sXPath : sXPath = "/XMLFeederRoot/Ad[Start_Date=End_Date]"
For Each n In XMLDoc.SelectNodes(sXpath)
set l = n.cloneNode(True)
q= l.selectSingleNode("/End_Date").text
strSDat=SplitArray(q)
l.removeChild(l.childNodes.item(2))
set Stat= l.selectSingleNode("/Status")
set Parent= Stat.parentNode
set EDate= xmlDoc2.createElement("End_Date")
EDate.appendChild xmlDoc2.createTextNode(strSDat)
Parent.insertBefore EDate, Stat
xmldoc2.documentElement.appendChild parent
Next
xmlDoc2.save xmldoc2.url
Function SplitArray(strval1)
dim SplitArray1, SplitArray2, strSDat
splitArray1 = split(strval1, "-")
splitArray1(2) = left(splitArray1(2), 2)
strDate1 = SplitArray1(1) & "/" & SplitArray1(2) & "/" & SplitArray1(0)
strDate30 = DateAdd("d", 30, strDate1)
SplitArray2 = split(strDate30, "/")
'Add zero to the left
If Len(SplitArray2(0))<2 Then
SplitArray2(0)= Right("0" & SplitArray2(0), 2)
End If
If Len(SplitArray2(1))<2 Then
SplitArray2(1)= Right("0" & SplitArray2(1), 2)
End If
SplitArray2(1) = splitArray2(1) & "T00:00:00"
strSDat = SplitArray2(2) & "-" & SplitArray2(0) & "-" & SplitArray2(1)
SplitArray= strSDat
End Function
Output File:
<?xml version="1.0" encoding="utf-8"?>
<XMLFeederRoot><Ad>
<Ad_Number>d00524224</Ad_Number>
<Start_Date>2016-08-20T00:00:00</Start_Date>
<End_Date>2016-09-19T00:00:00</End_Date><Status>Run</Status>
</Ad><Ad>
<Ad_Number>d00524225</Ad_Number>
<Start_Date>2016-08-20T00:00:00</Start_Date>
<End_Date>2016-09-19T00:00:00</End_Date><Status>Run</Status>
</Ad>
</XMLFeederRoot>
Since Text and XML files don't use file locks by default just overwrite the original file using xmlDoc.Save monvidPath.
Sub setAdEndDate(monvidPath)
Const sXPath = "/XMLFeederRoot/Ad/End_Date"
Set xmlDoc = CreateObject("Microsoft.XMLDOM")
xmlDoc.Async = "False"
xmlDoc.Load(monvidPath)
Set colNodes=xmlDoc.selectNodes(sXPath)
For Each n In colNodes
n.Text = SplitArray(n.Text)
Next
xmlDoc.Save monvidPath
End Sub
Function SplitArray(strval1)
Dim SplitArray1, SplitArray2, strSDat
splitArray1 = Split(strval1, "-")
splitArray1(2) = Left(splitArray1(2), 2)
strDate1 = SplitArray1(1) & "/" & SplitArray1(2) & "/" & SplitArray1(0)
strDate30 = DateAdd("d", 30, strDate1)
SplitArray2 = Split(strDate30, "/")
'Add zero to the left
If Len(SplitArray2(0))<2 Then
SplitArray2(0)= Right("0" & SplitArray2(0), 2)
End If
If Len(SplitArray2(1))<2 Then
SplitArray2(1)= Right("0" & SplitArray2(1), 2)
End If
SplitArray2(1) = splitArray2(1) & "T00:00:00"
strSDat = SplitArray2(2) & "-" & SplitArray2(0) & "-" & SplitArray2(1)
SplitArray= strSDat
End Function

Altering a script to write the results to a file

I have the following script which is gathering all of the information I need but I was wondering how I alter it to print all of the results that are displayed in the windows to a list of some sort? I would like to manipulate this data in excel.
Dim arrHeaders(35)
Set objShell = CreateObject("Shell.Application")
Set objFolder = objShell.Namespace("C:directory")
For i = 0 to 34
arrHeaders(i) = objFolder.GetDetailsOf(objFolder.Items, i)
Next
For Each strFileName in objFolder.Items
For i = 0 to 34
Wscript.Echo i & vbtab & arrHeaders(i) _
& ": " & objFolder.GetDetailsOf(strFileName, i)
Next
Next
Collect the detail information in another array and echo each record as a comma-separated line:
Dim arrData(35)
...
WScript.Echo Join(arrHeaders, ",")
For Each strFileName in objFolder.Items
For i = 0 to 34
arrData(i) = objFolder.GetDetailsOf(strFileName, i)
Next
WScript.Echo Join(arrData, ",")
Next
That way you can redirect the output to a CSV file by running the script with cscript.exe like this:
cscript //NoLogo "C:\path\to\your.vbs" > "C:\path\to\your.csv"
The CSV file can then be opened with Excel.
Another option is to use the FileSystemObject to write the output file from within the script:
Dim arrData(35)
...
Dim fso : Set fso = CreateObject("Scripting.FileSystemObject")
Set f = fso.OpenTextFile("C:\path\to\your.csv", 2, True)
f.WriteLine Join(arrHeaders, ",")
For Each strFileName in objFolder.Items
For i = 0 to 34
arrData(i) = objFolder.GetDetailsOf(strFileName, i)
Next
f.WriteLine Join(arrData, ",")
Next
f.Close
You don't need to modify the script to send the output to a file. Just open a console, navigate to script location and run the script, redirecting the output from console to a file
myvbscript.vbs > myoutputfile.txt
See:
http://www.microsoft.com/resources/documentation/windows/xp/all/proddocs/en-us/redirection.mspx?mfr=true

Find and replace string in my text with VBScript

I am searching for a VBScript that does a search and replace in files (e.g. 1.txt 2.xml).
I have file "1.txt" that inside there is the word "temporary" and I want to change it to "permanent".
Because I get this file a lot I need a script for it.
Every time that I try to write a script that contains open a txt file and the command replace, it doesn't.
I found a script that change this file with another file and does the change inside, but this is not what I am looking for.
Try this
If WScript.Arguments.Count <> 3 then
WScript.Echo "usage: Find_And_replace.vbs filename word_to_find replace_with "
WScript.Quit
end If
FindAndReplace WScript.Arguments.Item(0), WScript.Arguments.Item(1), WScript.Arguments.Item(2)
WScript.Echo "Operation Complete"
function FindAndReplace(strFilename, strFind, strReplace)
Set inputFile = CreateObject("Scripting.FileSystemObject").OpenTextFile(strFilename, 1)
strInputFile = inputFile.ReadAll
inputFile.Close
Set inputFile = Nothing
Set outputFile = CreateObject("Scripting.FileSystemObject").OpenTextFile(strFilename,2,true)
outputFile.Write Replace(strInputFile, strFind, strReplace)
outputFile.Close
Set outputFile = Nothing
end function
Save this in a file called Find_And_Replace.vbs, it can then be used at the command line like this.
[C:\]> Find_And_Replace.vbs "C:\1.txt" "temporary" "permanent"
*This method is case sensitive "This" != "this"
If you don't want to read the entire file into memory, you could use a temp file like this.
If WScript.Arguments.Count <> 3 then
WScript.Echo "usage: Find_And_replace.vbs filename word_to_find replace_with "
WScript.Quit
end If
FindAndReplace WScript.Arguments.Item(0), WScript.Arguments.Item(1), WScript.Arguments.Item(2)
WScript.Echo "Operation Complete"
function FindAndReplace(strFile, strFind, strReplace)
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objInputFile = objFSO.OpenTextFile(strFile,1)
strTempDir = objFSO.GetSpecialFolder(2)
Set objTempFile = objFSO.OpenTextFile(strTempDir & "\temp.txt",2,true)
do until objInputFile.AtEndOfStream
objTempFile.WriteLine(Replace(objInputFile.ReadLine, strFind, strReplace))
loop
objInputFile.Close
Set objInputFile = Nothing
objTempFile.Close
Set objTempFile = Nothing
objFSO.DeleteFile strFile, true
objFSO.MoveFile strTempDir & "\temp.txt", strFile
Set objFSO = Nothing
end function
You can try this version which doesn't slurp the whole file into memory:
Set objFS = CreateObject("Scripting.FileSystemObject")
strFile=WScript.Arguments.Item(0)
strOld=WScript.Arguments.Item(1)
strNew=WScript.Arguments.Item(2)
Set objFile = objFS.OpenTextFile(strFile)
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
if Instr(strLine,strOld)> 0 Then
strLine=Replace(strLine,strOld,strNew)
End If
WScript.Echo strLine
Loop
Usage:
c:\test> cscript //nologo find_replace.vbs file oldtext newtext

Resources