Computer Hope

Software => Computer programming => Topic started by: kdb2012 on March 21, 2012, 02:19:37 PM

Title: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post 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!
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 21, 2012, 02:23:56 PM
Why don't you put what is really in the file?
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 21, 2012, 02:47:30 PM
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
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 21, 2012, 03:04:03 PM
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?

Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 21, 2012, 03:23:48 PM
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.
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 22, 2012, 10:10:22 AM
Any Progress?
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Sidewinder on March 23, 2012, 11:37:40 AM
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.

Code: [Select]
$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)
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 23, 2012, 11:50:20 AM
Well done, Sidewinder!
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 23, 2012, 02:51:26 PM
Excellent! Thanks you sooo much.
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 23, 2012, 03:17:24 PM
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...

Code: [Select]
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...

Code: [Select]
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...

Code: [Select]
Kalvin Rodger = KALDB\AREADER
Kalvin Rodger = KALDB\PREADER
Kalvin Rodger = KALDB\READER
Kalvin Rodger = KALDB\SWRITER
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 23, 2012, 04:15:34 PM
Viola great my friend.
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Sidewinder on March 25, 2012, 07:29:08 AM
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:

Code: [Select]
$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)
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 25, 2012, 07:49:59 AM
I've been prettying up my effort too...

Code: [Select]

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

Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 26, 2012, 04:04:41 PM
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)
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 27, 2012, 12:02:44 AM
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?
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 27, 2012, 09:29:35 AM
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!
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 27, 2012, 10:28:47 AM
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?
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Sidewinder on March 27, 2012, 02:09:57 PM
Quote
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)

Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Salmon Trout on March 27, 2012, 03:38:27 PM
See the CR/LF?

(http://i124.photobucket.com/albums/p29/badoit/VBCapture.jpg)

Code: [Select]
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]'

Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Sidewinder on March 27, 2012, 04:46:59 PM
Quote
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)
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 27, 2012, 04:50:53 PM
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
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 27, 2012, 04:57:44 PM
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

Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: oldun on March 27, 2012, 08:55:53 PM
Try this:
Code: [Select]
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
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: Sidewinder on March 28, 2012, 06:13:49 AM
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:

Code: [Select]
#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
Title: Re: calculate from values in a text file's first column, ":" delimited, bat, VBs, PS
Post by: kdb2012 on March 28, 2012, 09:25:24 AM
@Oldun,
remarkably fast and accurate.

Thank you a lot.

@SideWinder
this is perfect version, cheers!

All of you deserve Kudos  ;)