|
|
|||||||
Seems like ages since I last visited as I hardly do much scripting anymore as everything is working! Hope that everyone is alive and well and enjoying life. I have encountered a problem that really needs resolving. Years ago when I setup my existing clients, I gave the servers the same names at each client so as to make scripting easy in that my current global coding is $ServerName = "\\Serverwhatever" etc. This made it easy to just use $servername everywhere in the script for Wsus, locations to text files required by the scripts etc. However, with new clients coming onboard this year and with them using different names for their servers, the Global Coding $ServerName = "\\Serverwhatever" in my udfs.kix file is not practical as I have to keep changing the "\\serverwhatever" to whatever the server name is in the udf file. I use the same script for every client, so I only have one script to update. Why do you guys do to address this issue as I don't think a readline (handle) type coding works in global. But if I want to install a printer for example, I need to have \\servername\printername in the script. I do a readline (handle) for the printer name, no problem, but thats not global and I need the server names global as there are many lines of coding pointing to \\servername\location whether is wsus, printer, a specific directory, mapped drive etc. Thanks |
||||||||
|
|
|||||||
Use INI files, dude. The tools are built into kix for easy manipulation. Better yet, Glenn wrote complete replacements UDFs for manipulating INI files as arrays. These things are slick as butter, have been thoroughly tested, and kept up to date. There is a version here that is not the most current although I doubt you would run into any of the issues that were fixed afterwords: http://www.kixtart.org/forums/ubbthreads.php?ubb=showflat&Number=202790#Post202790 Might need Glenn to post a link to the newer version as his link in the UDF page is broken now |
||||||||
|
|
|||||||
Until Glenn can update all his links to where ever his new site is, here is the current version. Code: ;; ;;=====================================================================================----- ;; ;;FUNCTION IniArray() ;; ReadIniArray() - subfunction for reading array ;; WriteIniArray() - subfunction for writing array ;; ;;ACTION Reads INI file into an array; ;; Writes array to INI format file & reloads the array with fresh data ;; ReadIniArray() reads a Section:Key:Data set from the array ;; WriteIniArray() writes a Section:Key:Data set to the array ;; ;;AUTHOR Glenn Barnas ;; ;;VERSION 1.3 - 2014/06/23 - Fixed issue when data contained equal signs. ;; 1.2 - 2013/04/20 - Read empty section or file, write empty section, ;; WriteIniArray() - fix error if duplicate write of null data Added Options ;; to IniArray for Write to add blank lines between sections. ;; 1.1 - 2013/04/17 - Improve file load time on large files. ;; 1.0 - 2011/08/02 ;; ;;SYNTAX IniArray(filename [,array] [,options]) ;; ;;PARAMETERS Filename - REQUIRED - String ;; - The name of the INI file to read/write ;; ;; Array - OPTIONAL - Array ;; - The properly formatted array to write ;; ;; Options - OPTIONAL - Integer ;; - A bitwise settings to control output formatting ;; Value Mode Description ;; 1 Write Adds blank lines between Sections when true ;; 2 Write Suppress the array reload on write, useful when no further INI manipulation is planned. ;; ;;REMARKS Reads an entire INI file into an array for processing. There is no ;; limit on the size of the INI file, other than any Kix limits on ;; general file sizes. On read, blank lines and comments (lines that ;; begin with ";" or "#") are ignored. On write, only sections that ;; contain data/value pairs are written, mimicing the action of ;; WriteProfileString(). Similarly, only Keys that have non-null ;; values are written. ;; ;; The secondary functions ReadIniArray() and WriteIniArray() make ;; using the IniArray() UDF as easy to use as ReadProfileString() ;; and WriteProfileString(), simply requiring calls to IniArray to ;; load and then save the INI file. The Read and Write sub-functions ;; use the same syntax as the ProfileString functions but reference ;; the array instead of the INI file. ;; ;; NOTE: The WriteIniArray function returns the updated array. This ;; is the only significant deviation from the WriteProfileString operation. ;; ;; NOTE: During array manipulation, deleted records are set to null ;; and not reused. Similarly, when a new key/data pair is added, deleted ;; array items are not reused. When the array is written to disk, the ;; null records are skipped. When the file is again read, the array will ;; only contain valid data with no empty records. ;; WRITING an array causes a RE-READ operation, cleaning the empty records. ;; ;; NOTE: IMPORTANT - When using WriteIniArray() to create a new Ini-array, ;; you MUST first declare the array via Dim $array[1, 0]. This is not ;; necessary if you read an INI file first. ;; ;;RETURNS Returns a two-dimensional array of two-dimensional arrays. The first ;; element is the section name, the second element is a two-dimensional ;; array consisting of the key/data pairs. ;; ;; Consider the following simple INI file: ;; [COLORS] ;; Apple=Red ;; Lime=Green ;; [TASTES] ;; Apple=sweet ;; Lime=sour ;; ;; The call $aINI = IniArray('inifile.ini) would return an array that ;; contained the following values: ;; $aINI[0,0] contains "COLORS" ;; $aINI[1,0] contains an array of data/value pairs from this section ;; - extract with $aData = $aINI[1,0] ;; $aData[0,0] contains "Apple"; $aData[1,0] contains "Red" ;; $aData[0,1] contains "Lime"; $aData[1,1] contains "Green" ;; ;; $aINI[0,1] contains "TASTES" ;; $aINI[1,1] contains Array of data/value pairs from this section ;; - extract with $aData = $aINI[1,1] ;; $aData[0,0] contains "Apple"; $aData[1,0] contains "Sweet" ;; $aData[0,1] contains "Lime"; $aData[1,1] contains "Sour" ;; ;; ; the following would return "Sweet", just like ReadProfileString ;; $Taste = ReadIniArray($aINI, 'TASTES', 'Apple') ;; ;; ;;DEPENDENCIES none ;; ;;TESTED WITH W2K, WXP, W2K3, W2K8, W7 ;; ;;EXAMPLES INI FILE READ: ;; $aIni = IniArray('testfile.ini') ;; 'File contains ' UBound($aIni, 2) + 1 ' sections' ? ;; ;; ENUMERATE: ;; For $I = 0 to UBound($aIni, 2) ;; $aData = $aIni[1, $I] ;; ;; 'Section: ' $aIni[0, $I] ' contains ' UBound($aData, 2) + 1 ' Data/Value elements' ? ;; ' Press a key: ' get $ ? ;; ;; For $J = 0 to UBound($aData, 2) ;; $J ': ' $aData[0, $J] ' = ' $aData[1, $J] ? ;; Next ;; Next ;; ;; ELEMENT READ: ;; ; Return a specific value ;; $Value = ReadIniArray($aIni, 'SectionName', 'keyname') ;; ; Return an array of all section names ;; $aSections = Split(ReadIniArray($aIni), Chr(10)) ;; ; Return an array of Keys in a specific section ;; $aKeys = Split(ReadIniArray($aIni, 'SectionName'), Chr(10)) ;; ;; ELEMENT WRITE/UPDATE: ;; ; Write a key/value pair in the named section ;; $aIni = WriteIniArray($aIni, 'SectionName', 'keyname', 'Data') ;; ;; ELEMENT DELETE: ;; ; Remove the named key from the defined section ;; $aIni = WriteIniArray($aIni, 'SectionName', 'keyname') ;; ;; SECTION DELETE: ;; ; Remove all key/value pairs from the section, deleting the section ;; $aIni = WriteIniArray($aIni, 'SectionName') ;; ;; INI FILE WRITE: ;; ; Flush the array to the file and then reload the array ;; $aIni = IniArray('testfile.ini', $aIni) ; Function IniArray($_fSrcFile, OPTIONAL $_aDataWrite, OPTIONAL $_Options) Dim $_ ; temp var Dim $_Fp ; file pointer Dim $_C, $_I, $_J, $K ; index pointers Dim $_AddLine, $_NoRead ; Option Vars Dim $_Sect ; Section Name Dim $_aDat ; Data pair array Dim $_aSect[1, 499], $_aData[1, 499] ; Section & Data Arrays $_NoRead = 0 ; perform read after write If $_Options If $_Options & 1 $_AddLine = @CRLF EndIf If $_Options & 2 $_NoRead = 1 ; write & exit w/o re-reading EndIf EndIf ; Obtain a File Handle for Read or Write operations ; ============================================================ $_Fp = FreeFileHandle ; locate an available file handle If Not $_Fp Exit 1 ; none available - exit EndIf ; WRITE: verify that we have properly formatted data ; ============================================================ If VarType($_aDataWrite) > 0 ; Write the array to the INI file and exit If VarType($_aDataWrite) < 8192 ; data but not an array - exit! Exit 87 EndIf Del $_fSrcFile ; delete any pre-existing file $_ = Open($_Fp, $_fSrcFile, 5) ; open the file for write/create If @ERROR Exit @ERROR EndIf ; exit if error opening ; Write the section data. If no data exists in the section, do not write anything! For $_I = 0 to UBound($_aDataWrite, 2) $_Sect = $_aDataWrite[0, $_I] ; Section name to write $_aData = $_aDataWrite[1, $_I] ; Data array If UBound($_aData, 2) > -1 ; create Sect and write data if data is present $_ = '[' + $_Sect + ']' + @CRLF ; section name $_C = 0 For $_J = 0 to UBound($_aData, 2) ; key/data pairs If $_aData[1, $_J] ; only write keys that have non-null data $_C = 1 $_ = $_ + $_aData[0, $_J] + '=' + $_aData[1, $_J] + @CRLF EndIf Next If $_C $_ = WriteLine($_Fp, $_ + $_AddLine) ; write the output line EndIf If @ERROR $_ = Close($_Fp) ; close the file Exit @ERROR EndIf ; exit if error writing EndIf Next $_ = Close($_Fp) ; close the file ReDim $_aData[1, 0] ; clear array If $_NoRead exit 0 ; exit here without a re-read of the freshly written data EndIf EndIf ; READ: Load the ini file into an array ; ============================================================ $_I = -1 $_J = -1 ; Initialize index pointers $_ = Open($_Fp, $_fSrcFile, 2) ; open the file for read If @ERROR ReDim $_aSect[1, 0] ReDim $_aData[1, 0] $_aSect[1, 0] = $_aData $IniArray = $_aSect ; return an empty array Exit @ERROR ; exit if error opening EndIf ReDim $_aSect[1, 499], $_aData[1, 499] ; Prep Section & Data Arrays for Read $_ = Trim(ReadLine($_Fp)) ; remove leading/trailing spaces While Not @ERROR ; loop through the file contents If Left($_, 1) = '[' ; found a section If $_I >= 0 ; process prior section data, if any If $_J >= 0 ReDim Preserve $_aData[1, $_J] ; trim data array to size $_aSect[1, $_I] = $_aData ReDim $_aData[1, 499] ; create the $_J = -1 Else $_I = $_I - 1 ; ignore empty sections EndIf EndIf $_I = $_I + 1 ; increment the section index If $_I Mod 499 = 0 And $_I > 0 ReDim Preserve $_aSect[1, $_I + 500] EndIf $_aSect[0, $_I] = Split(SubStr($_, 2), ']')[0] Else If Not InStr(';#', Left($_, 1)) And Len($_) > 2 $_aDat = Split($_, '=') ; break into array If UBound($_aDat) > 1 ; does data have embedded = signs? $_ = $_aDat[1] For $K = 2 to UBound($_aDat) ; extract them into a single value $_ = $_ + '=' + $_aDat[$K] Next $_aDat[1] = $_ ReDim Preserve $_aDat[1] EndIf $_J = $_J + 1 ; increment the data index If $_J Mod 499 = 0 And $_J > 0 ReDim Preserve $_aData[1, $_J + 500] EndIf $_aData[0, $_J] = $_aDat[0] ; Store the value $_aData[1, $_J] = $_aDat[1] ; Store the data EndIf EndIf $_ = Trim(ReadLine($_Fp)) ; remove leading/trailing spaces Loop ; done with input data $_ = Close($_Fp) ; close the file $_ = 2 ; prep for Not Found exit ; process the last/only section If $_I >= 0 If $_J >= 0 ReDim Preserve $_aData[1, $_J] ; trim data array to size $_aSect[1, $_I] = $_aData Else ReDim Preserve $_aData[1, 0] $_aSect[1, $_I] = $_aData EndIf ReDim Preserve $_aSect[1, $_I] ; trim section array to size $_ = 0 EndIf $IniArray = $_aSect ; return the array Exit $_ ; exit success EndFunction Function ReadIniArray($_aIniFile, OPTIONAL $_Section, OPTIONAL $_Key) ; exit immediately if the data format is invalid If VarType($_aIniFile) < 8192 ; data but not an array - exit! Exit 87 EndIf Dim $_aSectIdx[UBound($_aIniFile, 2)] ; Section Index Array Dim $_I, $_J ; Index pointers Dim $_aData ; Array of Key/Data pairs ; Create a section index array For $_I = 0 to UBound($_aIniFile, 2) $_aSectIdx[$_I] = $_aIniFile[0, $_I] Next ; If the Section is null, return a delimited string of Sections [same as ReadProfileString(file)] If Not $_Section $ReadIniArray = Join($_aSectIdx, Chr(10)) Exit 0 EndIf ; Search the index for a section $_I = aScan($_aSectIdx, $_Section) If $_I < 0 Exit 2 EndIf ; section not found - Exit $_aData = $_aIniFile[1, $_I] ; Extract the key/value array ; Create a Key index for the requested section Dim $_aKeyIdx[UBound($_aData, 2)] For $_J = 0 to UBound($_aData, 2) $_aKeyIdx[$_J] = $_aData[0, $_J] Next ; If the Key is null, return a delimited string of Keys [same as ReadProfileString(file, section)] If Not $_Key $ReadIniArray = Join($_aKeyIdx, Chr(10)) Exit 0 EndIf ; Search the index for a Key $_J = aScan($_aKeyIdx, $_Key) If $_J < 0 Exit 2 EndIf ; Key not found $ReadIniArray = $_aData[1, $_J] Exit 0 EndFunction Function WriteIniArray($_aIniFile, $_Section, OPTIONAL $_Key, OPTIONAL $_Data) ; exit immediately if the data format is invalid If VarType($_aIniFile) < 8192 ; data but not an array - exit! Exit 87 EndIf Dim $_aSectIdx[UBound($_aIniFile, 2)] ; Section Index Array Dim $_I, $_J ; Index pointers Dim $_aData ; Key/Data array ; Create a section index array For $_I = 0 to UBound($_aIniFile, 2) $_aSectIdx[$_I] = $_aIniFile[0, $_I] Next ; Search the index for a section $_I = aScan($_aSectIdx, $_Section) ; If the section does not exist and Keydata is present - add the section and key/value pair (new Sect:Key:Data) If $_I < 0 ; section not found - Add if Data is present If $_Key And $_Data $_I = 0 ; default to empty array If $_aIniFile[0, 0] $_I = UBound($_aIniFile, 2) + 1 ; find next section index EndIf ReDim Preserve $_aIniFile[1, $_I] ; Add new section ReDim $_aData[1, 0] ; create data array $_aData[0,0] = $_Key ; key $_aData[1,0] = $_Data ; data $_aIniFile[0, $_I] = $_Section ; section name $_aIniFile[1, $_I] = $_aData ; section data Else Exit 0 ; nothing to do EndIf Else ; the section does exist, locate the Key ; If the Key is null, delete the section (delete Section) If Not $_Key ReDim $_aData[1, 0] ; create empty keys array $_aIniFile[1, $_I] = $_aData ; write to section $_aIniFile[0, $_I] = '' ; write null section name Else $_aData = $_aIniFile[1, $_I] ; Extract the key/value array ; Create a Key index for the requested section Dim $_aKeyIdx[UBound($_aData, 2)] For $_J = 0 to UBound($_aData, 2) $_aKeyIdx[$_J] = $_aData[0, $_J] ; create the index Next $_J = aScan($_aKeyIdx, $_Key) ; find the key ; If the key does not exist, add the key/data (new Key/data) If $_J < 0 If $_Data $_aData = $_aIniFile[1, $_I] ; array of key/data pairs $_J = UBound($_aData, 2) + 1 ; find next key index ReDim Preserve $_aData[1, $_J] ; create data array $_aData[0, $_J] = $_Key ; key $_aData[1, $_J] = $_Data ; data $_aIniFile[1, $_I] = $_aData ; section data Else $WriteIniArray = $_aIniFile ; return original array Exit 0 ; nothing to do (no data) EndIf Else ; the key exists - either Update or Delete ; if the data is not null, write the new data (update key) If $_Data $_aData[1, $_J] = $_Data ; If the data is null, write empty key and data values (delete key) Else $_aData[0, $_J] = '' ; delete key $_aData[1, $_J] = '' ; clear data value EndIf $_aIniFile[1, $_I] = $_aData ; update the array EndIf EndIf EndIf $WriteIniArray = $_aIniFile Exit 0 EndFunction |
||||||||
|
|
|||||||
Hi Allen, Sorry to take so long to get back to you. I was off ill for a few days. I will have a look and see if using ini files will do what I need. Many thanks |
||||||||
|
|
|||||||
It takes a little practice getting going with the INI files but they really do make it easy to make modifications to scripts. If you need any assistance show us some code and we will get you in the right direction. |
||||||||
|
|
|||||||
When I teach my scripting class, one of the guidelines is to consider your data elements, plan how they are organized, then write the code to load, manipulate, and then possibly write/update the results. I do this with every script no matter how simple. For Kix, I create the INI file to hold and manage my data before the script writing begins. Next, I write the script header and fill out blocks of comments to describe each processing component. Finally, I flesh out the logic, block by block, testing as I go. Takes a bit longer to complete development, but I'm using Kix scripts to perform monitoring, maintenance, and alert processing for thousands of endpoints. FYI - the IniArray is slightly slower than direct reads, which is why I don't use it in my login script (where performance must parallel reliability), but most other applications that have more than a section or two do use it. LARGE (over 64K) INI files do read slowly, but there's no other way to use them. Allen's got some pretty large INI files he manages with this UDF. Glenn |
||||||||
|
|
|||||||
Ya I think that I am going to need to review my script and see where I need to have the name of a server inputted as the ini file that Allen has provided above is a very very long script and I don't think that I need an array. All I need in the actual script is to say WSUS server is $Servername + "\xyz" kind of thing. I just need to find a way to define $servername. I might have a look at how I did the printers as I created text files with the printer names for each client and all I did was run the udf at that point in the script. I can do the same thing for the servername in theory. In addition I can perhaps do @domain\path name instead of \\servername\pathname. I will review this when I go on holiday next week. |
||||||||
|
|
|||||||
Something like this: Code: [Drive G] PATH=\\server\folder NAME=The cool kids share DISK=G: GROUPS=group1,group2 Then, simple ReadProfileString() functions are all you need. Code: ; Get a list of drive mappings from the INI file $aMappings = Splic(ReadProfileString('config.ini', '', ''), Chr(10)) For Each $Map in $aMappings $Path = ReadProfileString('config.ini', $Map, 'PATH') $DISK = ReadProfileString('config.ini', $Map, 'DISK') ; Get list of groups $Grps = ReadProfileString('config.ini', $Map, 'GROUPS') $OK = 1 ; OK to map this resource If $Grps ; if Grps is defined, check for membership If Not InGroup($Grps) $OK = 0 ; not a member, so NOT OK EndIf EndIf If $OK ; OK to map resource USE $Disk $Path EndIf Next There are UDFs available that will read the INI and return a list of sections, but that's basically what the first step does - the list of drive mappings can then be enumerated easily and each processed one at a time. You should never worry about the size of a UDF - it does the job and you should never modify it - just add it to your code. I write simple scripts with just 10-15 lines of actual logic, then KGen identifies and includes all the UDFs that I used and the script becomes 1000+ lines - so what? I never look at the UDFs once they are written and placed into a script. As for "@DOMAIN", that will reference your DC, not a file server. Putting shares on a DC is a significant security risk. DCs should be small systems that provide security and DNS services ONLY. We don't install any apps (like Acrobat, Zip/rar, net scan), services (no DHCP even!), or other non-security roles. IAS/RADIUS is one of the only features we might add to a DC. When we bring on a new client, we clean up the DC to meet security standards, as more and more companies are being audited by their customers to identify and mitigate risk of data breach. A dedicated file and app server and a few hours of consulting time is much less expensive than losing a client or dealing with a data breach. Glenn |