Computer Hope
Software => Computer programming => Topic started by: kdb2012 on March 21, 2012, 02:19:37 PM
-
Please help me program the scenario below:
contents of myfile.txt:
ServerName: KDB2012
----------------------------
2:Black\Bird
16:Black\Dog
20:Black\Cat
----------------------------
6:Animal
15:Animal
65:Animal
Now, I want to check "Animal" by its number, if that number (6 in our case) > 2 and < 16 (Black\whatever) then print Animal = Black\Bird
similarly if 15 > 16 and 15 < 20 then print Animal = Black\Dog
if 65 > 20 then print Animal = Black\Cat
I am in desperate need to do this, either using batch, VBs, Powershell. Much Appreciated!
-
Why don't you put what is really in the file?
-
ServerName: KDB2012\ADUsers
---------------------
2:KALDB\Developers
16:KALDB\ProdDevs
20:KALDB\BADATAREADER
25:KALDB\ZADATAREADER
115:KALDB\AREADER
168:KALDB\PREADER
190:KALDB\PWRITER
192:KALDB\READER
257:KALDB\WRITER
261:KALDB\SWRITER
299:KALDB\ITDataProc
316:KALDB\RDevelopment
340:KALDB\ReadOnly
-------------------------
152:Kalvin Rodger
183:Kalvin Rodger
239:Kalvin Rodger
289:Kalvin Rodger
-
There will be 4 lines printed
152:Kalvin Rodger
152 is greater than 115 and less than 168, so print Kalvin Rodger = KALDB\AREADER
183:Kalvin Rodger
183 is greater than 168 and less than 190 so print Kalvin Rodger = KALDB\PREADER
239:Kalvin Rodger
239 is greater than 192 and less than 257 so print Kalvin Rodger = KALDB\READER
289:Kalvin Rodger
289 is greater than 261 and less than 299 so print Kalvin Rodger = KALDB\SWRITER
Is that correct?
-
Correct!
And the contents of the file are dynamic. They will change from time to time. The groups and their number and the ADUser and its number will keep on changing.
-
Any Progress?
-
Any Progress?
Didn't know we were on the clock. :D
The hard part was retrieving chunks of the file for individual processing. VBScript was a possible solution as it supports regular expressions, but so does Powershell so I went with the latter.
$file = "c:\myfile.txt"
$arrTitles = @()
$arrEmps = @()
# Load the Job Titles into Array
#
(Get-Content -Path $file) -match "^[0-9]{1,3}:[A-Z]{1,}\\[A-Z]{1,}$" |
ForEach-Object {
$arrTitles += , @( [Convert]::ToDecimal($_.split(":")[0]), $_.split(":")[1] )
}
$arrTitles.GetEnumerator() | Sort-Object | Out-Null
# Load the Employees into Array
#
(Get-Content -Path $file) -match "^[0-9]{1,3}:[A-Z]{1,}\s[A-Z]{1,}$" |
ForEach-Object {
$arrEmps += , @( [Convert]::ToDecimal($_.split(":")[0]), $_.split(":")[1] )
}
#Do the Lookups
#
for($j=0; $j -lt $arrEmps.Count; $j++) {
for($i=0; $i -lt $arrTitles.Count - 1; $i++) {
if( ($arrEmps[$j][0] -ge $arrTitles[$i][0]) -and ($arrEmps[$j][0] -le $arrTitles[$i+1][0]) ) `
{ Write-Host $arrEmps[$j][1], "=", $arrTitles[$i][1] }
}
}
Save the script with a PS1 extension and run from the Powershell command prompt. Powershell does not search the current directory for a script so use the .\ pointer if you need to point to the current directory.
The first line of code is the file name. Change as needed.
8)
-
Well done, Sidewinder!
-
Excellent! Thanks you sooo much.
-
I just had to finish it...
Save with .vbs extension
call with cscript.exe //nologo
pass input filename as parameter
example
cscript //nologo Scriptname.vbs input.txt
input.txt...
ServerName: KDB2012\ADUsers
---------------------
2:KALDB\Developers
16:KALDB\ProdDevs
20:KALDB\BADATAREADER
25:KALDB\ZADATAREADER
115:KALDB\AREADER
168:KALDB\PREADER
190:KALDB\PWRITER
192:KALDB\READER
257:KALDB\WRITER
261:KALDB\SWRITER
299:KALDB\ITDataProc
316:KALDB\RDevelopment
340:KALDB\ReadOnly
-------------------------
152:Kalvin Rodger
183:Kalvin Rodger
239:Kalvin Rodger
289:Kalvin Rodger
The script...
Const ForReading = 1
Const ForWriting = 2
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set ReadFile = objFSO.OpenTextFile (wscript.arguments(0), ForReading)
strText = ReadFile.ReadAll
Readfile.Close
arrFileLines = Split(strText, vbCrLf)
i=3
l=0
Dim LimitList()
Do
sline=arrFileLines(i)
ReDim Preserve LimitList (l)
LimitList (l) = sline
l = l + 1
i = i + 1
Loop until Mid(sline,1,10)="----------"
Dim LimitValues()
Dim OutPutText()
For j = 0 To (UBound(LimitList)-1)
MyString=LimitList(j)
arrTokens = Split (MyString, ":")
ReDim Preserve LimitValues(j)
LimitValues(j) = arrtokens(0)
ReDim Preserve OutPutText(j)
OutPutText(j) = arrTokens(1)
Next
Dim LookupCode()
For j = 0 To (UBound(LimitValues)-1)
ReDim Preserve LookupCode(j)
LookUpCode (j) = "If (numval > " & LimitValues(j) & ") AND (numval < " & LimitValues(j+1) & ") Then wscript.echo nameval & " & Chr(34) & " = " & OutPutText(j) & Chr(34)
Next
ReDim Preserve LookupCode(j)
LookUpCode (j) = "If (numval > " & LimitValues(UBound(LimitValues)) & ") Then wscript.echo nameval & " & Chr(34) & " = " & OutPutText(j) & Chr(34)
For j = i To UBound(arrFileLines)
MyString = arrfilelines(j)
arrTokens = split(MyString,":")
numval=arrTokens(0)
Nameval=arrTokens(1)
For k = 0 To UBound(LookUpCode)
ExecuteGlobal LookUpCode(k)
Next
Next
output...
Kalvin Rodger = KALDB\AREADER
Kalvin Rodger = KALDB\PREADER
Kalvin Rodger = KALDB\READER
Kalvin Rodger = KALDB\SWRITER
-
Viola great my friend.
-
In the interest of accuracy, I have since discovered that the Powershell solution has a serious logic flaw and an improper use of the Sort-Object cmdlet. The script worked in spite of the flaws, but will produce inaccurate results under certain circumstances.
A newer and better version is posted below:
$file = Join-Path -Path $pwd -ChildPath myfile.txt
$arrTitles = @()
$arrEmps = @()
# Load the Job Titles into Array
#
(Get-Content -Path $file) -match "^[0-9]{1,3}:[A-Z]{1,}\\[A-Z]{1,}$" |
ForEach-Object {
$arrTitles += , @( ( [Convert]::ToDouble($_.split(":")[0]) ).ToString("000"), $_.split(":")[1] )
}
$arrTitles = $arrTitles | Sort-Object @{Expression={$_[0]}; Ascending=$true}
# Load the Employees into Array
#
(Get-Content -Path $file) -match "^[0-9]{1,3}:[A-Z]{1,}\s[A-Z]{1,}$" |
ForEach-Object {
$arrEmps += , @( ( [Convert]::ToDouble($_.split(":")[0]) ).ToString("000"), $_.split(":")[1] )
}
# Do the Lookups
#
for($j=0; $j -lt $arrEmps.Count; $j++) {
for($i=0; $i -lt $arrTitles.Count - 1; $i++) {
if( ($arrEmps[$j][0] -eq $arrTitles[$i+1][0]) ) `
{ Write-Host $arrEmps[$j][1], "=", $arrTitles[$i+1][1]; break }
if( ($arrEmps[$j][0] -ge $arrTitles[$i][0]) -and ($arrEmps[$j][0] -lt $arrTitles[$i+1][0]) ) `
{ Write-Host $arrEmps[$j][1], "=", $arrTitles[$i][1] }
}
}
Sorry for any inconvenience. 8)
-
I've been prettying up my effort too...
Const ForReading = 1
Set objFSO = CreateObject("Scripting.FileSystemObject")
' Read whole input file at once into string
Set ReadFile = objFSO.OpenTextFile (wscript.arguments(0), ForReading)
strText = ReadFile.ReadAll
Readfile.Close
' Split string into individual lines
arrFileLines = Split(strText, vbCrLf)
Dim LimitList()
Dim LimitValues()
Dim OutPutText()
' Get first part into array
' Parsed stored file up to second dashed line
' Start at 3rd line
InputLine=3
LimitLine=0
' Read file lines one by one
Do
sline=arrFileLines(InputLine)
ReDim Preserve LimitList (LimitLine)
LimitList (LimitLine) = sline
LimitLine = LimitLine + 1
InputLine = InputLine + 1
Loop until Mid(sline,1,10)="----------"
' InputLine now points to first line after dashes
DataStart = InputLine
' Get band limit values
' Read each line of first block
For j = 0 To (UBound(LimitList)-1)
' Split line into 2 tokens at : character
arrTokens = Split (LimitList(j), ":")
ReDim Preserve LimitValues(j)
' First token is numerical value
LimitValues(j) = arrtokens(0)
ReDim Preserve OutPutText(j)
' Second token is text string for that band
OutPutText(j) = arrTokens(1)
Next
' Process second part of input file
For j = DataStart To UBound(arrFileLines)
' Split line into 2 tokens at : character
arrTokens = split(arrfilelines(j),":")
numval=Int(arrTokens(0))
Nameval=arrTokens(1)
' For each line in second part
For k = 0 To UBound(Limitvalues)
LineToWrite = nameval & " = " & OutPutText(k)
' This is lower test value
lower = Int(Limitvalues(k))
' If not last line check if number is above this band lower limit
' and below next band lower limit
If k < UBound(Limitvalues) Then
' This Is next band lower limit
upper = Int(Limitvalues(k+1))
' Do test
If (numval > lower) AND (numval < upper) Then
wscript.echo LineToWrite
End If
Else
' If this is last line just test if number is above limit
If (numval > lower) Then
wscript.echo LineToWrite
End If
End If
Next
Next
-
Not sure why am I getting this:
RESULTS ARE OK, but
in old script
C:\Kaldb\adu.vbs(42, 4) Microsoft VBScript runtime error: Subscript out of rang
e: '[number: 0]'
in new script
C:\Kaleem\now.vbs(53, 2) Microsoft VBScript runtime error: Subscript out of rang
e: '[number: 0]'
53: numval=Int(arrTokens(0))
54: numval=arrTokens(1)
-
Not sure why am I getting this:
RESULTS ARE OK, but
in old script
C:\Kaldb\adu.vbs(42, 4) Microsoft VBScript runtime error: Subscript out of rang
e: '[number: 0]'
in new script
C:\Kaleem\now.vbs(53, 2) Microsoft VBScript runtime error: Subscript out of rang
e: '[number: 0]'
53: numval=Int(arrTokens(0))
54: numval=arrTokens(1)
Possibly there is a line in input file not as your description; best thing is to learn scripting and then you can write and fix scripts yourself. Does Sidewinder's script gives error?
-
Yes sidewinder's script throws errors, but I am concentrating more on VB as I have some prior experience. But as I mentioned in my first post that the files I receive are dynamic and the contents changes every time. But the overall pattern remains the same.
1: server name (may or may not have \instance)
2: -----------------------------
3:domain\AD_Group
...
n:domain\AD_Group
(n+1):--------------------------
(n+2):AD_User that belongs to AD_Group(s) above
...
m:AD_User that belongs to AD_Group(s) above
That's it.
Thanks!
-
The example text file you provided does not throw any errors with the script I posted, so something must be different. I wonder if there are extra blank lines at the end of the files that cause the error? Could you post an example of a file that gave the error messages?
-
Yes sidewinder's script throws errors, but I am concentrating more on VB as I have some prior experience.
I too would be interested to know what those errors are. I can't get it to fail with your original test file, or my souped up test file or even a test file with embedded blank records.
Posting an example of a test file that produced any errors would be most helpful.
8)
-
See the CR/LF?
(http://i124.photobucket.com/albums/p29/badoit/VBCapture.jpg)
C:\Batch\Test>Readfile-CH.vbs Input2.txt
Kalvin Rodger = KALDB\AREADER
Kalvin Rodger = KALDB\PREADER
Kalvin Rodger = KALDB\READER
Kalvin Rodger = KALDB\SWRITER
C:\Batch\Test\Readfile-CH.vbs(51, 4) Microsoft VBScript runtime error: Subscript out of range: '[number: 0]'
-
See the CR/LF?
Indeed I do. However the regular expression filtered out any records that did not match the pattern. Powershell was used to have access to a sort technique in case the AD_Group was not sequenced. I still can't get the Powershell version to fail even with the hanging CR/LF.
I know the OP prefers the VBS, just looking for why the PS version throws errors.
8)
-
This is the files I worked on today:
ServerName: KADB
------------------------------------
2:KALDB\AD Development
15:KALDB\DB-BDATAREADER)
20:KALDB\ETL_USERS
24:KALDB\API2
30:KALDB\prod_development
------------------------------------
Note: In this the user is not found (this line is not part of the file)
Yesterday it was this file
ServerName: KADB
------------------------------------
2:KALDB\AD QA
15:KALDB\BIREADER)
20:KALDB\ETL_USERS
24:KALDB\BIWriter
30:KALDB\dbowners
------------------------------------
3:Sue Ling
25:Sue Ling
31:Sue Heimer
-
With Sidewinder's script nothing is returned for this file:
ServerName: KADB
------------------------------------
2:KALDB\AD Development
15:KALDB\DB-BDATAREADER)
20:KALDB\ETL_USERS
24:KALDB\API2
30:KALDB\prod_development
------------------------------------
3:Sue Ling
25:Sue Ling
31:Sue Hemmer
-
Try this:
Option Explicit
Dim file, domain(), users(), i, j, temp, tmparr
If WScript.Arguments.Count=0 Then
WScript.Echo "Required filename not specified....Quitting"
wscript.quit()
End If
file=WScript.Arguments(0)
UpdateArrays file, domain, users
on error resume next
i=ubound(users)
if err.number then
wscript.Echo "No users...Quitting"
wscript.quit
end if
i=ubound(domain)
if err.number then
wscript.Echo "No domain...Quitting"
wscript.quit
end if
on error goto 0
for i=ubound(domain)-1 to 0 Step -1
for j=0 To i
if domain(j)>domain(j+1) then
temp=domain(j+1)
domain(j+1)=domain(j)
domain(j)=temp
end if
next
next
for i=0 To ubound(users)
for j=1 To ubound(domain)
if (domain(j)>users(i)) Then
tmparr=Split(Users(i),":")
temp=tmparr(1) & " = "
tmparr=split(domain(j-1),":")
temp=temp & tmparr(1)
wscript.echo temp
exit for
elseif users(i)>domain(ubound(domain)) then
tmparr=Split(Users(i),":")
temp=tmparr(1) & " = "
tmparr=split(domain(ubound(domain)),":")
temp=temp & tmparr(1)
wscript.echo temp
exit for
end if
next
next
Sub UpdateArrays(file,ByRef ar1,ByRef ar2)
Const ForReading=1
Dim fso,infile,i1,i2,line
Dim regex1,regex2,match,matches1,matches2
Set regex1=New RegExp
Set regex2=New RegExp
regex1.Pattern="^\d+:\w+\\\w+$"
regex2.Pattern="^\d+:\w+\s\w+$"
regex1.Global=True
regex2.Global=True
Set fso=CreateObject("Scripting.FileSystemObject")
if (Not fso.FileExists(file)) Then
WScript.Echo file & " does not exist...Quitting"
WScript.Quit()
End If
Set inFile=fso.OpenTextFile(file,ForReading)
i1=0
i2=0
Do While Not infile.AtEndOfStream
line=infile.ReadLine
Set matches1=regex1.Execute(line)
Set matches2=regex2.Execute(line)
for each match in matches1
redim preserve ar1(i1)
line=Split(line,":")
ar1(i1)=Right(String(4,"0") & line(0),4) & ":" & line(1)
i1=i1+1
next
for each match in matches2
redim preserve ar2(i2)
line=Split(line,":")
ar2(i2)=Right(String(4,"0") & line(0),4) & ":" & line(1)
i2=i2+1
next
loop
infile.close()
End Sub
-
I dislike unfinished business. Regular Expressions (RegEx) are difficult to read, difficult to write and difficult to maintain. When the data pattern changes, as it did in the second sample, the RegEx must change also. So with a little help from a RegEx tool, a tweak here and there and much headbanging, Version 3.0 has arrived:
#Requires -Version 2.0
$file = Join-Path -Path $pwd -ChildPath myFile.txt
$arrGroups = @()
$arrUsers = @()
# Load the Groups into Array
#
(Get-Content -Path $file) -match "^[0-9]{1,3}:[A-Za-z0-9]{1,}\\(\s|\S){1,}$" |
ForEach-Object {
$arrGroups += , @( [int] $_.split(":")[0], $_.split(":")[1] )
}
$arrGroups = $arrGroups | Sort-Object @{Expression={$_[0]}; Ascending=$true}
# Load the Users into Array
#
(Get-Content -Path $file) -match "^[0-9]{1,3}:[A-Z]{1,}\s[A-Z]{1,}$" |
ForEach-Object {
$arrUsers += , @( [int] $_.split(":")[0], $_.split(":")[1] )
}
# Do the Lookups
#
for($j=0; $j -lt $arrUsers.Count; $j++) {
for($i=0; $i -lt $arrGroups.Count - 1; $i++) {
if( ($arrUsers[$j][0] -eq $arrGroups[$i+1][0]) ) `
{ Write-Host $arrUsers[$j][1], "=", $arrGroups[$i+1][1]; break }
if( ($arrUsers[$j][0] -gt $arrGroups[$i][0]) -and ($arrUsers[$j][0] -lt $arrGroups[$i+1][0]) ) `
{ Write-Host $arrUsers[$j][1], "=", $arrGroups[$i][1]; break }
if( ($arrUsers[$j][0] -ge $arrGroups[$arrGroups.Count - 1][0]) ) `
{ Write-Host $arrUsers[$j][1], "=", $arrGroups[$arrGroups.Count-1][1]; break }
}
}
The $file variable holds the name of the input file. The default is to have both the script and the file in the current directory, so you can run the script as: .\scriptname.ps1
This can be changed by the user.
If the input data pattern changes again, you have an opportunity to make the changes. Good luck :D
-
@Oldun,
remarkably fast and accurate.
Thank you a lot.
@SideWinder
this is perfect version, cheers!
All of you deserve Kudos ;)