Powershell Compare Two Strings - powershell

cls
$logFile = "C:\test\output1.txt"
Function LogWrite
{
Param ([string]$logstring)
Add-content $Logfile -value $logstring
}
LogWrite "DocumentID|Correct|Wrong|UDI|Number of Errors|Line Number"
LogWrite "------------------------------------------"
$file = "C:\test\test\Birth records evt logging.txt"
$pattern = "^(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(COB Reviewed)$"
$pattern2 = "^(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(DocSecID)$"
$pattern3 = "^(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)`t(.*)$"
$errorCountTotal = 0
$linecount = 0
$line2Count = 0
Get-Content $file|
ForEach-Object{
$errorCountLine = 0
$linecount++
$transposition = $false
if($_ -match $pattern){
}elseif($_ -match $pattern2){
}elseif($_ -match $pattern3){
$line2Count++
if($matches[6].Length -eq $matches[7].length){
$wrong = $matches[6]
$correct = $matches[7]
$documentID = $matches[3]
$UDI = $matches[8]
$a = [char[]]$Matches[6]
$b = [char[]]$matches[7]
# for($i = 0; $i -lt $a.Length; $i++){
# for($x = 1; $x -lt $a.Length; $x++){
# if($a[$i] -eq $b[$i+$x] -and $a[$i+$x] -eq $b[$i]){
# if($a[$i] -eq $a[$i+$x]){
# write-host "same letter"
# }else{
# $errorCountLine += 2
# }
# }
# }
#}
#Compare-Object $a $b |Format-List |Out-File "C:\test\test3.txt"
$errorCountLine += (#(Compare-Object $a $b -SyncWindow 0).count /2)
$errorCountTotal +=$errorCountLine
Write-Host $matches[6] " - " $matches[7] " - " $errorCountLine " - " $linecount
Write-Host $errorCountTotal
LogWrite "$documentID|$wrong|$correct|$UDI|$errorCountLine|$linecount"
}else{
$a = [char[]]$Matches[6]
$b = [char[]]$matches[7]
for($i = 0; $i -lt $a.Length; $i++){
for($x = 1; $x -lt $a.Length; $x++){
if($a[$i] -eq $b[$i+$x] -and $a[$i+$x] -eq $b[$i]){
if($a[$i] -eq $a[$i+$x]){
# write-host "same letter"
}else{
$errorCountLine += 2
}
}
}
}
$diffL = [math]::Abs($Matches[7].Length - $Matches[6].Length)
$errorCountLine = (((#(Compare-Object $a $b).count-$diffL) /2) + $diffL)
$test = #(Compare-Object $a $b).count
$errorCountTotal += $errorCountLine
Write-Host $matches[6] " - " $matches[7] " - " $errorCountLine " - " $linecount
$wrong = $matches[6]
$correct = $matches[7]
$documentID = $matches[3]
$UDI = $matches[8]
LogWrite "$documentID|$wrong|$correct|$UDI|$errorCountLine|$linecount"
Write-Host $errorCountTotal
}
}
}
Write-Host $line2Count #number of lines that the program looks at. passes through pattern3.
LogWrite `n
LogWrite "The total number of errors is $errorCountTotal"
I need to compare the contents of two Strings with this program. Above is what i have so far. The only problem is it tells me whether or not the two strings match or not(0 or -1) character by character in the string. Midred and Mildred would come up as 5 errors when in fact it needs to be only 1 error. i cannot just compare the strings as a whole either because there could be multiple errors in a string. any ideas would be great.

Try using Compare-Object on the character arrays:
$a = [char[]]'Mildred'
$b = [char[]]'Midred'
Compare-Object $a $b
InputObject SideIndicator
----------- -------------
l <=
#(Compare-Object $a $b).count
1

Why not just compare the two strings, e.g.
$matches[6] -ne $ matches [7]
?

Related

Powershell - Move Mail

I'm newbie to powershell, and I have a challenge on process my script. It did everything perfectly, but it wont move the message to the target folder and I don't know why.
I thought maybe to process at end cycle, but the issue is that I can encour that will transfer also file that have not any reference, and I would avoid to process something that I have no info about.
I tried this
$MailboxName = "fatture#abc.com"
write-host "Now Processing" -ForegroundColor Yellow
write-host "Searching on Fatture" -ForegroundColor Yellow
$olFolderInbox = 6
$outlook = new-object -com outlook.application;
$namespace = $outlook.GetNameSpace("MAPI");
$recipient = $namespace.CreateRecipient($MailboxName)
$inbox = $namespace.GetSharedDefaultFolder($recipient,6)
$messages = $inbox.Folders.Item("Carriers").folders.Item("Gpac").folders.item("BLR")
$MoveTarget = $inbox.Folders.Item("Carriers").folders.Item("Gpac").folders.item("BLS")
$filepath = "C:\temp\FATTURE\Automation\"
$messages.Items | % {
Write-Host "New Message " $_.Subject
$msg = $_
$Ref = $_.Subject -replace ' ',''
$RefStr = $ref.split('#')
$SearchSTR = $RefStr[2]
write-host $SearchStr
# $invNbr = $body.Substring($body.IndexOf("INVOICE NO")+19,10)
#write-host "The Invoice Number is : $invNbr" -ForegroundColor Cyan
write-host "Querying for $SearchSTR" -ForegroundColor Green
$t = (Query_file($SearchSTR))[-1].CONSOL_REF
write-host "File Found : $t" -ForegroundColor Cyan
write-host $t
if ($t -ne $null) {
$filetype = $t.Substring(0,1)
$date = Get-Date -format "yyyyMMddhhmmss"
write-host "Today is Date is $date" -ForegroundColor Red
Switch($filetype.tostring())
{
{($_ -eq 1) -or ($_ -eq 2) -or ($_ -eq 7) -or ($_ -eq 7) -or ($_ -eq 8) }{($dept = "0001"), ($dept1 = "ZLAG")}
{($_ -eq "F") -or ($_ -eq "V") -or ($_ -eq "M")}{($dept = "0002"), ($dept1 = "Zero"), ($type = "MOR")}
{($_ -eq 3) -or ($_ -eq 5) -or ($_ -eq "J")} {($dept = "0028"), ($dept1 = "One"), ($type = "OBR") }
{($_ -eq 9)} {($dept = "0031"), ($dept1 = "CONSOLE")}
}
write-host "The KEY TYPE is $dept1 " -ForegroundColor Yellow
$fileStr = $t + "_INV_" + $dept + "_$date_.tsk"
write-host " FILE Path: " $fileStr -ForegroundColor Cyan
$msg.attachments | ?{$_.filename -like "*.pdf"} | %{
$file = $filestr
$_.saveasfile((Join-Path $filepath $filestr))
}
$msg.UnRead = $false
$msg.Move($MoveTarget)
}
#fine cycle messages
}
I tried to move from the cycle but the issue is that it goes for all of email, instead, I want to process only the email that should be. I would like when the attachment has being downloaded, that the email will be moved.

Is there a way to speed up dynamic member look-up in ps-objects

In the following code, most time is spent in $v = $proc.$columnName and I am wondering if there
is a way to speed up looking up the objects's members's values.
In the code below, I have chosen $objs to be the result of get-process but in my case, $objs could be an array of any type of objects, thus the need to look up the objects's members dynamically.
$objs= get-process
$columnNames = #()
foreach ($member in ($objs | get-member -memberType property, noteproperty)) {
[string]$name = $member.name
$columnNames += $name
}
[Int64 ] $sum = 0
[string] $columnName = ''
foreach ($obj in $objs) {
foreach ($columnName in $columnNames) {
$v = $obj.$columnName
# $v = $obj.psObject.members.Item($columnName).value
if ($v -eq $null) {
}
elseif ($v -is [System.IntPtr]) {
$sum = $sum + ($v -as [int64] )
}
elseif ($v -is [System.Int64] -or $v -is [System.Int32]) {
$sum = $sum + $v
}
}
}
"sum = $sum"
Perhaps there are more ways to speed this up, but below I have taken out the unnecessary bits:
$objs= Get-Process
$columnNames = ($objs | Get-Member -MemberType property, noteproperty).Name
[Int64 ] $sum = 0
foreach ($obj in $objs) {
foreach ($v in $columnNames) {
if ($obj.$v -as [int64]) { $sum += [int64]$obj.$v }
}
}
"sum = $sum"
This is a hidden property on PSObjects called .PSObject that way too many people don't know about, including myself until a few weeks ago despite spending thousands of hours working with PowerShell. From there you can use .where to SIGNIFICANTLY increase filtering performance.
New Code Block
New-Variable -Force -Name:'Processes' -value:(Get-Process)
New-Variable -Force -Name:'Sum' -Value:([Int64]$Null)
ForEach ($Process in $Processes) {
$Process.PSObject.Properties.Where({
($_.MemberType -in #('Property','NoteProperty')) -and
($_.TypeNameOfValue -in #('System.IntPtr','System.Int64','System.Int32')) -and
(-Not [String]::IsNullOrWhiteSpace($_.value))
}) |ForEach-Object {
$Sum = $Sum + ($_.value -as [Int64])
}
}
"Sum = $Sum"
Comparison Result
Name Value
---- -----
BlockBTime 9.18
SameResult True
BlockATime 1.52
BlockASum 1037197387512388
Difference Block A (New Code) is ~7.66s faster than Block B (Old Code)
BlockBSum 1037197387512388
Validation & Comparison
#Stopwatch
New-Variable -Force -Name:'StopWatch' -Value:(New-Object -TypeName 'System.Diagnostics.Stopwatch')
New-Variable -Force -Name:'Result' -Value:#{
BlockATime = [Int]0
BlockASum = [Int64]0
BlockBTime = [Int]0
BlockBSum = [Int64]0
Difference = $Null
SameResult = $Null
}
New-Variable -Force -Name:'Processes' -value:(Get-Process)
$StopWatch.Restart()
New-Variable -Force -Name:'Sum' -Value:([Int64]$Null)
ForEach ($Process in $Processes) {
$Process.PSObject.Properties.Where({
($_.MemberType -in #('Property','NoteProperty')) -and
($_.TypeNameOfValue -in #('System.IntPtr','System.Int64','System.Int32')) -and
(-Not [String]::IsNullOrWhiteSpace($_.value))
}) |ForEach-Object {
$Sum = $Sum + ($_.value -as [Int64])
}
}
$Result.BlockATime = [math]::Round($StopWatch.Elapsed.TotalSeconds,2)
$Result.BlockASum = $Sum
$objs= $Processes
$StopWatch.Restart()
$columnNames = #()
foreach ($member in ($objs | get-member -memberType property, noteproperty)) {
[string]$name = $member.name
$columnNames += $name
}
[Int64 ] $sum = 0
[string] $columnName = ''
foreach ($obj in $objs) {
foreach ($columnName in $columnNames) {
$v = $obj.$columnName
# $v = $obj.psObject.members.Item($columnName).value
if ($v -eq $null) {
}
elseif ($v -is [System.IntPtr]) {
$sum = $sum + ($v -as [int64] )
}
elseif ($v -is [System.Int64] -or $v -is [System.Int32]) {
$sum = $sum + $v
}
}
}
$Result.BlockbTime = [math]::Round($StopWatch.Elapsed.TotalSeconds,2)
$Result.BlockbSum = $Sum
#Which block is faster?
If ($Result.BlockATime -lt $Result.BlockBTime) {
$Result.Difference = "Block A (New Code) is ~$(($Result.BlockBTime - $Result.BlockATime))s faster than Block B (Old Code)"
} Else {
$Result.Difference = "Block A (New Code) is ~$(($Result.BlockBTime - $Result.BlockATime))s Slower than Block B (Old Code)"
}
#Are the results the same?
If ($Result.BlockASum -eq $Result.BlockBSum) {
$Result.SameResult = $True
}

Powershell - How would I return appname from a foreach loop only if status code -ne 200?

The title says it all. I have been returning output from every iteration of both of the below foreach loops (see the Write-Host and Write-Output lines), but the application that is using this script (Nagios) can't handle that much data. Therefore, I would like to only return 1 output at a time. Basically "All apps okay" or "app(s) down: then list the apps not returning 200 response code".
I have no idea how to do this as getting the foreach loops to work in the first place was quite the challenge for me.
$ok = 0
$warning = 1
$critical = 2
$unknown = 3
$appPool = get-webapplication
$errorcode = 0
$time_errorcode = 0
foreach($a in $appPool) {
$app = $a.Attributes[0].Value;
$url = "http://localhost$app/apitest/index"
$HTTP_Request = [System.Net.WebRequest]::Create($url)
$HTTP_Response = try{ $HTTP_Request.GetResponse() }catch {$exceptionMessage = $_.Exception.Message
$exceptionItem = $app}
[int]$HTTP_Response.StatusCode -ne 200
$statuscode = [int]$HTTP_Response.StatusCode
Write-Host "$app status code: $statuscode"
if ($HTTP_Response.StatusCode.value__ -ne 200) {
[int]$errorcode = 1
}
}
foreach($t in $appPool){
$app = $t.Attributes[0].Value;
$url = "http://localhost$app/apitest/index"
$output = "$PSScriptRoot\10meg.test"
$start_time = Get-Date
try {Invoke-WebRequest -Uri $url -OutFile $output} catch {
$exceptionMessage = $_.Exception.Message
$exceptionItem = $app
Write-Output "$app error: $exceptionMessage"}
Write-Output "$app Time taken: $((Get-Date).Subtract($start_time).milliSeconds) millisecond(s)"
$timetaken = $((Get-Date).Subtract($start_time).milliSeconds)
if ($timetaken.StatusCode.value__ -ge 500) {
[int]$time_errorcode = 1
}
}
#Uncomment for testing
#Write-Output $time_errorcode
#Write-Output $errorcode
if (($errorcode -eq 0 -and $time_errorcode -eq 0)){exit $ok}
if (($errorcode -eq 1 -and $time_errorcode -eq 0)){exit $critical}
if (($errorcode -eq 0 -and $time_errorcode -eq 1)){exit $warning}
if (($errorcode -eq 1 -and $time_errorcode -eq 1)){exit $critical}
else {exit $unknown}
Here's my approach. UNTESTED. Just to give you an idea to work with.
$appPool = Get-WebApplication
$errorcode = 0
$time_errorcode = 0
# Using a hashset ensures every app is contained only once
$appsDown = New-Object "System.Collections.Generic.HashSet[string]"
foreach($a in $appPool) {
$app = $a.Attributes[0].Value;
$url = "http://localhost$app/apitest/index"
### Test response code ###
$HTTP_Request = [System.Net.WebRequest]::Create($url)
$HTTP_Response = $null
try {
$HTTP_Response = $HTTP_Request.GetResponse()
} catch {
# for test
Write-Host $_
}
if ($null -eq $HTTP_Response -or [int]$HTTP_Response.StatusCode -ne 200 ) {
[int]$errorcode = 1
[void]$appsDown.Add($app)
}
### Test response time ###
$output = "$PSScriptRoot\10meg.test"
$start_time = Get-Date
$timetaken = -1
try {
Invoke-WebRequest -Uri $url -OutFile $output
$timetaken = ((Get-Date) - $start_time).TotalMilliSeconds
} catch {
# for test
Write-Host $_
}
if ($timetaken -lt 0 -or $timetaken -ge 500) {
[int]$time_errorcode = 1
[void]$appsDown.Add($app)
}
}
# Output the results here
if ($appsDown.Count -eq 0) {
Write-Output "All apps okay"
}
else {
Write-Output ($appsDown.Count.ToString() + "app(s) down")
$appsDown | sort | foreach {
Write-Output $_
}
}
if (($errorcode -eq 0 -and $time_errorcode -eq 0)){exit $ok}
if (($errorcode -eq 1 -and $time_errorcode -eq 0)){exit $critical}
if (($errorcode -eq 0 -and $time_errorcode -eq 1)){exit $warning}
if (($errorcode -eq 1 -and $time_errorcode -eq 1)){exit $critical}
else {exit $unknown}
Explanations:
$appsDown = New-Object "System.Collections.Generic.HashSet[string]"
Create a new instance of a .NET HashSet to hold the app names. (It's an unordered collection where every value is stored only once.)
[void]$appsDown.Add($app)
Add the app name to the collection. [void] is there to prevent the method's return value to be sent to the pipeline.
This may help you:
$list = #()
Foreach(x in y){
$item = #{}
If(x.error -eq 200){
$item.Name = x.Name
}
$obj = New-Object PSObject -Property $item
$list += $obj
}
Adding these parts will leave you with a list of appsnames in the variable $list

My script runs in powershell ISE but not powershell console?

function Get-LoggedOnUsers ($server) {
if($server -eq $null){
$server = "localhost"
}
$users = #()
# Query using quser, 2>$null to hide "No users exists...", then skip to the next server
$quser = quser /server:$server 2>$null
if(!($quser)){
Continue
}
#Remove column headers
$quser = $quser[1..$($quser.Count)]
foreach($user in $quser){
$usersObj = [PSCustomObject]#{Server=$null;Username=$null;SessionName=$null;SessionId=$Null;SessionState=$null;LogonTime=$null;IdleTime=$null}
$quserData = $user -split "\s+"
#We have to splice the array if the session is disconnected (as the SESSIONNAME column quserData[2] is empty)
if(($user | select-string "Disc") -ne $null){
#User is disconnected
$quserData = ($quserData[0..1],"null",$quserData[2..($quserData.Length -1)]) -split "\s+"
}
# Server
$usersObj.Server = $server
# Username
$usersObj.Username = $quserData[1]
# SessionName
$usersObj.SessionName = $quserData[2]
# SessionID
$usersObj.SessionID = $quserData[3]
# SessionState
$usersObj.SessionState = $quserData[4]
# IdleTime
$quserData[5] = $quserData[5] -replace "\+",":" -replace "\.","0:0" -replace "Disc","0:0"
if($quserData[5] -like "*:*"){
$usersObj.IdleTime = [timespan]"$($quserData[5])"
}elseif($quserData[5] -eq "." -or $quserData[5] -eq "none"){
$usersObj.idleTime = [timespan]"0:0"
}else{
$usersObj.IdleTime = [timespan]"0:$($quserData[5])"
}
# LogonTime
$usersObj.LogonTime = (Get-Date "$($quserData[6]) $($quserData[7]) $($quserData[8] )")
$users += $usersObj
}
return $users
}
function LogOutUser {
$test = Get-LoggedOnUsers
$count = 0
$nameArray = #()
$sessionArray = #()
$logonTimeArray = #()
foreach($line in $test){
$field1 = $line.Username
$field2 = $line.SessionID
$field3 = $line.LogonTime
$nameArray += $field1
$sessionArray += $field2
$logonTimeArray += $field3
$count+=1
}
$session1 = [INT]$sessionArray[0]
$session2 = [INT]$sessionArray[1]
Write-Host $nameArray
Write-Host $sessionArray
Write-Host $logonTimeArray
Write-Host $count
Write-Host $session1
Write-Host $session2
if(($count -gt 1) -and ($session2 -gt $session1 )){
logoff $session1
}
}
Get-LoggedOnUsers
LogOutUser
this code works in the ISE but it does not work when running from the console, what is happening? It does exactly what i want it to do through the ISE but when I try running as a scheduled task it doesn't work also it doesnt work when trying to run with console. i have tried powershell.exe -noexit -file c:\logofftest1.ps1 but still no success

Powershell Scripting Issues (Newbie)

Ok, am having some issues with my script, they probably are pretty apparent but here is the script. Would greatly appreciate a review of the script with some feedback as why they are broken.
## Script Startup
$Computers = Get-Content .\computers.txt
$Total = $Computers.length
$consoleObject = (Get-Host).UI.RawUI
## Set Variables
$Run = 0
$Successful = 0
# Checks to see if there is a log file already created. If so check number of successful computer ran against, if not, create the log file.
IF ( test-path .\~temp.txt ) {
$Log = Get-Content .\~temp.txt
ForEach ($LogLine in $Log) {
$LogCheck = $LogLine.ToString().Split(",")[2]
IF ( "$LogCheck" -eq "Successful" ) {
$Successful += 1
}
}
} ELSE {
add-content .\~temp.txt "Computer Name,Attempts,Last Attempt,Time,Date"
}
while ( "$Completed" -le "$total" ) {
$Run += 1
$Time = Get-Date
$consoleObject.WindowTitle = “Admin Check - $Successful Out Of $Total Successful `| Run`: $Run”
ForEach ($Computer in $Computers) {
IF ( Select-String .\~temp.txt -pattern "$Computer" -quiet ) {
$LogUpdate = Select-String .\~Temp.txt -pattern $Computer
$Attempts = $LogUpdate.ToString().Split(",")[1]
$Result = $LogUpdate.ToString().Split(",")[3]
} ELSE {
add-content .\~temp.txt "$Computer,0,Not Checked,Not Run Yet,Not Run Yet"
$Attempts = ""
$Result = ""
}
IF ( "$Result" -eq "Successful") {
write-output "$Computer Already Completed"
} ELSE {
IF ( test-connection $Computer -quiet ) {
# Command Here
$Successful += 1
$IsOn = "True"
} ELSE {
$IsOn = "False"
}
$Attempts += 1
}
( Get-Content .\~temp.txt ) | Foreach-Object {$_ -replace "$Computer,.*", ($Computer + "," + $Attempts + "," + $IsOn + "," + $Result + "," + $Time.ToShortTimeString() + "," + $Time.ToShortDateString())} | Set-Content .\~temp.txt
}
}
~temp.txt
Computer Name,Attempts,Last Attempt Result,Time,Date
52qkkgw-94210jv,11111111111111,False,,8:47 PM,10/27/2012
HELLBOMBS-PC,11111111111111111111111111111111111111111111111111111111111,True,,8:47 PM,10/27/2012
52qkkgw-94210dv,11111111111111111111111111111111111111111111111111111111,False,,8:46 PM,10/27/2012
Current Issue:
- Doesn't actually stop when successful equals total.
- doesn't check result vs successful so keeps redoing all comps forever.
- The Attempts tab just keeps adding 1 to the last time or something, so it makes some weird continuous "1111" thing.
Probably more just havn't noticed them yet.
Here's working code:
clear
## Script Startup
$Computers = Get-Content \computers.txt
cd c:\pst
$Total = $Computers.length
$consoleObject = (Get-Host).UI.RawUI
## Set Variables
$Run = 1
$Successful = 0
# Checks to see if there is a log file already created. If so check number of successful computer ran against, if not, create the log file.
IF ( test-path .\~temp.txt ) {
$Log = Get-Content .\~temp.txt
ForEach ($LogLine in $Log) {
$LogCheck = $LogLine.ToString().Split(",")[2]
IF ( "$LogCheck" -eq "Successful" ) {
$Successful += 1
}
}
} ELSE {
add-content .\~temp.txt "Computer Name,Attempts,Last Attempt,Time,Date"
}
while ( $Run -ne $total ) {
$Run += 1
$Time = Get-Date
$consoleObject.WindowTitle = “Admin Check - $Successful Out Of $Total Successful `| Run`: $Run”
ForEach ($Computer in $Computers) {
IF ( Select-String .\~temp.txt -pattern "$Computer" -quiet ) {
$LogUpdate = Select-String .\~Temp.txt -pattern $Computer
$Attempts = $LogUpdate.ToString().Split(",")[1]
$Result = $LogUpdate.ToString().Split(",")[3]
} ELSE {
add-content .\~temp.txt "$Computer,0,Not Checked,Not Run Yet,Not Run Yet"
$Attempts = ""
$Result = ""
}
IF ( "$Result" -eq "Successful") {
write-output "$Computer Already Completed"
} ELSE {
IF ( test-connection $Computer -quiet ) {
# Command Here
$Successful += 1
$IsOn = "True"
} ELSE {
$IsOn = "False"
}
$Attempts += 1
}
( Get-Content .\~temp.txt ) | Foreach-Object {$_ -replace "$Computer,.*", ($Computer + "," + $Attempts + "," + $IsOn + "," + $Result + "," + $Time.ToShortTimeString() + "," + $Time.ToShortDateString())} | Set-Content .\~temp.txt
}
}