|
|
|||||||||
|
|||||||||
|
|||||||||
| |
||
| |||||||||
![]() |
|
|
«
Previous Thread
|
Next Thread
»
|
Thread Tools | Search this Thread | Rate Thread | Display Modes |
|
#1
|
|||
|
|||
|
Hello everyone
I have a Perl script I wrote that works very nicely but before I leave in October my team need it converted to Windows Script as I seem to be the only one who knows any Perl (I thought about PowerShell is out, as strictly it should not be installed on our corporate XP image, same for ActivePerl so no PerlScript engine). Unfortunately I don't usually work with Windows, so I'm pretty new to Windows Script and I'd really appreciate some pointers. I don't necessarily need actual code or somebody to write the script for me, just a general outline of what functions, etc I might need but if there is some sample code around, great. I have some template .xml files that contain certain strings to be substituted, generating one "real" xml file per country. In a file the pipe-delimited country data (i.e. | ) we decide which template to use (currently only 2 types, numbered 1 and 2). What I need to do is roughly: - Define string "SCX00" (Dim?, then set value, I suppose) - Define a counter starting at 100 - read the country data file, e.g. the first few lines might be DE|Germany|1 IE|Ireland|1 ES|Spain|2 etc... The file won't ever go beyond about 240 lines. I believe Code:
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFile = objFSO.OpenTextFile("countries.txt", ForReading)
Do Until objFile.AtEndOfStream
strLine = objFile.ReadLine
arrFields = Split(strLine, "|")
strContents = strContents & arrFields(0) & arrFields(1) & arrFields(2) & vbCrlf
Loop
does that for a start. - Put this lot into an array (will strContents above be seen as an array or just a big string?) - For each row in that array: - we decide what template to use (i.e. currently either file 1.xml or 2.xml) - create a new file based on that template (make a copy of the template under a certain name, so I guess objFSObject.CopyFile is used there) - make some string substitutions in this file, based on the current value of the counter (e.g. one will be SCX00100, SCX00101... etc), country code and country name. VBScript doesn't seem to have a one-liner for this, do I have to read the file as a string, search'n'replace, then write out as a string again? - close the new file - increase the counter So not complicated but I seem to have a mental block. Thanks for any pointers. Cheers |
|
#2
|
||||
|
||||
|
Can you post your existing Perl code? I know Perl and it would be a lot easier to see exactly what you're trying to do.
__________________
Click the image if at any point you don't like my decision.Scripting problems? Windows questions? Ask the Windows Guru! |
|
#3
|
|||
|
|||
|
Quote:
No probs, here you go: Code:
#!/usr/bin/perl -w
use strict;
use File::Copy;
my $counter = 100;
my $serialbit = "SCX00";
my $country_data_file = "countries.txt";
my ($country, $countrycode, $countryname, $templatetype);
my ($template, $newfile, $infile);
my @country_data;
# open an array with the data for each country
open(DAT, $country_data_file) || die ("Could not open file!");
@country_data=<DAT>;
close(DAT);
chomp(@country_data);
foreach $country (@country_data)
{
($countrycode,$countryname,$templatetype)=split(/\|/,$country);
# template 1 has 4 devices (C6000, C4000-C, C4000-E, MODC300)
# template 2 has 5 devices (C6000, C4000-C, C4000-E, C711-C, MODC300)
# we could define a third template, etc with different devices...
# copy the appropriate template ready for use
$template = "DummyCL$templatetype\.xml";
$newfile = "dummyCL\/dummycl_$countrycode\.xml";
copy($template, $newfile) or die "File cannot be copied!";
# now we have to do the substitutions
open (IN, "$newfile") || die("Error Reading File: $newfile $!");
{
undef $/;
$infile = <IN>;
}
close (IN) || die("Error Closing File: $newfile $!");
# the substitutions we want to make
$infile =~ s/P_COUNTRYCODE/$countrycode/g;
$infile =~ s/P_COUNTRYNAME/$countryname/g;
$infile =~ s/P_SERIALNO/$serialbit$counter/g;
$infile =~ s/P_C6000SERIALNO/0$counter-01/g;
$infile =~ s/P_C4000CSERIALNO/0$counter-02/g;
$infile =~ s/P_C4000ESERIALNO/0$counter-03/g;
$infile =~ s/P_C711CSERIALNO/0$counter-04/g;
$infile =~ s/P_MODC300SERIALNO/0$counter-05/g;
# open the new file and insert the substituted text
open (PROD, ">$newfile") || die("Error Writing to File: $newfile $!");
print PROD $infile;
close (PROD) || die("Error Closing File: $newfile $!");
# increase the counter
$counter++;
}
|
|
#4
|
||||
|
||||
|
Cool. Looks simple enough. I'll post back. There's two ways of doing this. I can convert this code directly and do text substitutions as you have, but WSH has the ability to work with XML files directly as well. If you can post your XML files, I can show you both ways. That may be a more efficient method. In the meantime, I'll do a straight conversion.
|
|
#5
|
|||
|
|||
|
I managed to whip this lame attempt up. Maybe it's useful for someone. The whole "open and close the file for each string replacement" is very inefficient and I'm sure will make many of you cringe (although, I don't get the impression WSH is the best tool for this sort of job). It was suggested to me that because the target files are XML I should use XSLT transformations. Well, I was never much of a programmer, but it works for now...
The output files have new lines added to them for some reason but I'm not going to spend more time on this now.Code:
Option Explicit
Dim intCounter
Dim strSerialBit
Dim strCountryCode, strCountryName, intTemplateType, arrCountryData
Dim strTemplate, strInFile, strNewFile
Dim objFSO, objCountryDataFile, objFile, objInFile, objNewFile
Dim strText, strNewText
Dim strNextLine
Const ForReading = 1
Const ForWriting = 2
Const ForAppending = 8
'
' FUNCTIONS
'
Function fnStringReplace(strFile, strSearch, strReplace)
Set objFile = objFSO.OpenTextFile(strFile, ForReading)
strText = objFile.ReadAll
objFile.Close
strNewText = Replace(strText, strSearch, strReplace)
Set objFile = objFSO.OpenTextFile(strFile, ForWriting)
objFile.WriteLine strNewText
objFile.Close
End Function
'
' MAIN SECTION
'
intCounter = 100
strSerialBit = "SCX00"
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objCountryDataFile = objFSO.OpenTextFile ("countries.txt", ForReading)
Do Until objCountryDataFile.AtEndOfStream
strNextLine = objCountryDataFile.Readline
arrCountryData = Split(strNextLine , "|")
strCountryCode = arrCountryData(0)
strCountryName = arrCountryData(1)
intTemplateType = arrCountryData(2)
strInFile = "DummyCL" & intTemplateType & ".xml"
strNewFile = "dummyCL\dummycl_" & strCountryCode & ".xml"
objNewFile = objFSO.CopyFile (strInFile, strNewFile, True)
Call fnStringReplace (strNewFile, "P_COUNTRYCODE", strCountryCode)
Call fnStringReplace (strNewFile, "P_COUNTRYNAME", strCountryName)
Call fnStringReplace (strNewFile, "P_SERIALNO", "SCX00" & intCounter)
Call fnStringReplace (strNewFile, "P_C6000SERIALNO", "0" & intCounter & "-01")
Call fnStringReplace (strNewFile, "P_C4000CSERIALNO", "0" & intCounter & "-02")
Call fnStringReplace (strNewFile, "P_C4000ESERIALNO", "0" & intCounter & "-03")
Call fnStringReplace (strNewFile, "P_C711CSERIALNO", "0" & intCounter & "-04")
Call fnStringReplace (strNewFile, "P_MODC300SERIALNO", "0" & intCounter & "-05")
intCounter = intCounter + 1
Loop
Set objFSO = Nothing
Set objCountryDataFile = Nothing
Set objInFile = Nothing
Set objNewFile = Nothing
|
|
#6
|
||||
|
||||
|
I apologize for not getting back as quickly as I had hoped. I'm actually putting together more simplified code for you. Rather than performing multiple string replacements, I'm using regular expressions to do the replacements. This allows you to performs them more efficiently.
Also, there is no need to open and close the file between each string replacement. This should be a little more clear when you see my code. I'll go into more detail when I post the solution. In the meantime, I'm glad you were able to figure out a working solution. It looks like you're well on your way to becoming a Windows scripter! |
|
#7
|
|||
|
|||
|
Oh and just for reference this is DummyCL1.xml (the other one is pretty much the same).
It's one of these situations when you're trying to find a balance between quick'n'dirty and elegant ![]() Code:
<?xml version="1.0"?> <RIGMonitoring Version="2.10"> <LabDef IsModified="true"> <MessageHeader FillerId="P_SERIALNO.Smartcom.e_library_training.ACME.P_COUN TRYCODE" TimeCreated="2006-04-11T01:23:42+01:00"/> <LabIdentifier LabId="e_library_training.ACME.P_COUNTRYCODE"/> <LabRegistrator CountryCode="P_COUNTRYCODE" LabName="e_library_training" WorldregionCode="" CountryregionCode="" OrganisationCode=""/> <LabDetails> <Contact Contacttype="Customer" Name="ACME" Address1="any address" Address2="" City="ACME" Country="P_COUNTRYNAME" PostalCode="11111"/> <Contact Contacttype="HeadOfLab" Name="Any Contact" Email="" Phone1="()" Fax=""/> <Contact Contacttype="KeyOperator" Name="any contact" Email="" Phone1="()" Fax="()"/> <Contact Contacttype="FSR1" Name="Any FSR" Email="any_fsr@ACME.com" Phone1="0" Fax=""/> <Contact Contacttype="FSR2" Name="" Email="" Phone1="" Fax=""/> <Contact Contacttype="ACMESupport" Name="any name" Email="any_name@ACME.com" Phone1="0" Fax="0"/> </LabDetails> </LabDef> <InstDef> <LabIdentifier LabId="e_library_training.ACME.P_COUNTRYCODE"/> <InstIdentifier InstrumentId="P_C6000SERIALNO.COBAS6000.e_library_training.ACME. P_COUNTRYCODE"/> <InstRegistrator SystemclassCode="c6000" SystemclassVersion="" SerialNo="P_C6000SERIALNO" HardwareKey="Unable to detect"> <ModuleComposition> <ModuleRegistrator ModuleNo="1" HardwareKey="Unable to detect" SerialNo="P_C6000SERIALNO" ModuletypeCode="60C501"/> <ModuleRegistrator ModuleNo="2" HardwareKey="Unable to detect" SerialNo="P_C6000SERIALNO" ModuletypeCode="60E601"/> </ModuleComposition> </InstRegistrator> <InstDetails SVProcessorSerialNo="P_SERIALNO" SVProcessorDialupNo=""/> </InstDef> <InstDef> <LabIdentifier LabId="e_library_training.ACME.P_COUNTRYCODE"/> <InstIdentifier InstrumentId="P_C4000CSERIALNO.c4000-c.e_library_training.ACME.P_COUNTRYCODE"/> <InstRegistrator SystemclassCode="c4000-c" SystemclassVersion="" SerialNo="P_C4000CSERIALNO" HardwareKey="Unable to detect"> <ModuleComposition> <ModuleRegistrator ModuleNo="1" HardwareKey="Unable to detect" SerialNo="P_C4000CSERIALNO" ModuletypeCode="6"/> <ModuleRegistrator ModuleNo="2" HardwareKey="Unable to detect" SerialNo="P_C4000CSERIALNO" ModuletypeCode="modC300"/> </ModuleComposition> </InstRegistrator> <InstDetails SVProcessorSerialNo="P_SERIALNO" SVProcessorDialupNo=""/> </InstDef> <InstDef> <LabIdentifier LabId="e_library_training.ACME.P_COUNTRYCODE"/> <InstIdentifier InstrumentId="P_C4000ESERIALNO.c4000-e.e_library_training.ACME.P_COUNTRYCODE"/> <InstRegistrator SystemclassCode="c4000-e" SystemclassVersion="" SerialNo="P_C4000ESERIALNO" HardwareKey="Unable to detect"> <ModuleComposition> <ModuleRegistrator ModuleNo="1" HardwareKey="Unable to detect" SerialNo="P_C4000ESERIALNO" ModuletypeCode="3"/> <ModuleRegistrator ModuleNo="2" HardwareKey="Unable to detect" SerialNo="P_C4000ESERIALNO" ModuletypeCode="modC300"/> </ModuleComposition> </InstRegistrator> <InstDetails SVProcessorSerialNo="P_SERIALNO" SVProcessorDialupNo=""/> </InstDef> <InstDef> <LabIdentifier LabId="e_library_training.ACME.P_COUNTRYCODE"/> <InstIdentifier InstrumentId="P_MODC300SERIALNO.MODANA.e_library_training.ACME.P _COUNTRYCODE"/> <InstRegistrator SystemclassCode="MODANA" SystemclassVersion="" SerialNo="P_MODC300SERIALNO" HardwareKey="Unable to detect"> <ModuleComposition> <ModuleRegistrator ModuleNo="1" HardwareKey="Unable to detect" SerialNo="P_MODC300SERIALNO" ModuletypeCode="modC300"/> <ModuleRegistrator ModuleNo="2" HardwareKey="Unable to detect" SerialNo="P_MODC300SERIALNO" ModuletypeCode="1"/> <ModuleRegistrator ModuleNo="2" HardwareKey="Unable to detect" SerialNo="P_MODC300SERIALNO" ModuletypeCode="2"/> <ModuleRegistrator ModuleNo="2" HardwareKey="Unable to detect" SerialNo="P_MODC300SERIALNO" ModuletypeCode="3"/> </ModuleComposition> </InstRegistrator> <InstDetails SVProcessorSerialNo="P_SERIALNO" SVProcessorDialupNo=""/> </InstDef> <InstDef> <LabIdentifier LabId="e_library_training.ACME.P_COUNTRYCODE"/> <InstIdentifier InstrumentId="P_SERIALNO.Smartcom.e_library_training.ACME.P_COUN TRYCODE"/> <InstRegistrator SystemclassCode="smartcom" SystemclassVersion="4.0.0.0510011.512f" SerialNo="P_SERIALNO" HardwareKey=""> <ModuleComposition/> </InstRegistrator> <InstDetails SVProcessorSerialNo="P_SERIALNO" SVProcessorDialupNo=""/> </InstDef> </RIGMonitoring> |
|
#8
|
||||
|
||||
|
Awesome. That helps a lot.
|
|
#9
|
|||
|
|||
|
XML DOM method
I would highly encourage you to use the XML DOM (http://msdn.microsoft.com/en-us/library/ms763742(VS.85).aspx)to do this task. I have provided links to XML references on MSDN, and some examples following the pseudocode algorithm.
Initial steps (you're going to have to figure this stuff out on your own I don't know enough about what you're doing): Create an XML document with all of your data. This would contain, say a basic LabDef section and a basic InstDef section inside of your document element RIGMonitoring) Create one (or possibly more) XSLT stylesheets to transform the data as needed. Place blank nodes for country code, country name and counter in this file. This would also contain processing instructions to produce the requisite number and types of InstDef nodes for each output file. Script: Code:
Instantiate 3 XML document objects.
Load XML data file into document object 1.
Open CountryData file for reading
Until CountryData.EOF Do
Read Line from Country Data
Load appropriate XSLT file into document object 2.
Use DOM methods to change the country code, country
name and counter in the XSLT object.
Call the TransformNode method of Document Object 1 to
transform the data into a new xml document using
the XSLT document object as input and the 3rd
document object as output.
Use the Save method of the output to save to a file path.
Set the xml property of the stylesheet and output objects
to an empty string to start fresh for next iteration.
Loop
This will give you the shortest script because everything is being done in the transform. I think this would be the most flexible option. However, if you're not willing to figure out the XSLT transform functions, you could also just use the SelectNodes functions and getAttributes functions shown below to directly modify the XML templates. To instantiate a document object: set objXMLDoc = CreateObject("Msxml2.DOMDocument.5.0") To load a file into the object objXMLDoc.load "yourfile.xml" To select an XML node using xpath (xpath reference is here http://msdn.microsoft.com/en-us/library/ms256115(VS.85).aspx)) :set objNode= objXMLDoc.documentElement.SelectSingleNode("xpath statment") To get an attribute object: set objAttribute=objNode.GetAttributeNode("attribute name") Use the Value property of the attribute node to set or retrieve the value of the attribute: strProperty=objAttribute.Value or objAttribute.Value=strProperty Perform a transform using a stylesheet: objOutputDoc.xml = objXMLDoc.TransformNode(objStyleSheetDoc) Save changes to a document object: objOutputDoc.Save "FilePath" |
![]() |
| Viewing: ASP Free Forums > System Administration > Windows Scripting > VBScript - Pointers on script to produce a series of files from templates |
| Thread Tools | Search this Thread |
| Display Modes | Rate This Thread |
|
|