PowerShell - Find and replace multiple patterns to anonymize file - powershell

I need you help. I have a log.txt file with various data in it which I have to anonymize.
I would like to retrieve all these "strings" matching a predefined patterns, and replace these by another values for each of them. What is important is that each new string from the same pattern (and with different value from the previous) should be replaced by the predefined value increased by +1 (e.g. "orderID = 123ABC" becomes "orderID = order1" and "orderID=456ABC" becomes "orderID=order2").
The patterns to search for are more than 20 so it is not possible to put them all in single line.
My idea is:
Define "patterns.txt" file
Define "replace.txt" file ("pattern" value and replacement value)
Search for all "patterns" in the log file, the result will be ARRAY
Find the unique entries in that ARRAY
Get the "replacement" value for each unique entry in the ARRAY
Replace all occurrences in log.txt. The tricky part here is that any occurrence of the same type (but different value from the previous one) needs to be incremented by (+1) in order to be different from the one before.
Example of what I have :
requestID>qwerty1-qwerty2-qwerty3</requestID
requestID>12345a-12345b-12345c</requestID
requestID>qwerty1-qwerty2-qwerty3</requestID
requestID>qwerty1-qwerty2-qwerty3</requestID
orderID>012345ABCDE</orderID
orderID>012345ABCDE</orderID
orderID>ABCDE012345</orderID
orderID>ABCDE012345</orderID
keyId>XYZ123</keyId
keyId>ABC987</keyId
keyId>XYZ123</keyId
Desired result:
requestID>Request-1</requestID
requestID>Request-2</requestID
requestID>Request-1</requestID
requestID>Request-1</requestID
orderID>Order-1</orderID
orderID>Order-1</orderID
orderID>Order-2</orderID
orderID>Order-2</orderID
keyId>Key-1</keyId
keyId>Key-2</keyId
keyId>Key-1</keyId
For the moment I managed only to find the unique values per type:
$N = "C:\FindAndReplace\input.txt"
$Patterns = "C:\FindAndReplace\pattern.txt"
(Select-String $N -Pattern 'requestID>\w{6}-\w{6}-\w{6}</requestID>').Matches.Value | Sort-Object -Descending -Unique
(Select-String $N -Pattern '<orderID>\w{20}</orderID>').Matches.Value | Sort-Object -Descending -Unique
(Select-String $N -Pattern '<keyId>\w{8}</keyId>').Matches.Value | Sort-Object -Descending -Unique
Thanks in advance for any suggestion on how to progress.

Your patterns don't match your sample data. I've corrected the patterns to accommodate the actual sample data.
It seems a simple hash table per type would fulfill the need to keep track of matches and counts. If we process the log file with a switch statement using the -Regex and -File parameters we can work on each line at a time. The logic for each is
Check if the current match exists in the specific type's match array.
If not, add it with it's replacement value (type-count) and increment count.
If it does exist, use the already defined replacement value.
Capture all the output in a variable and then write it out to file when done.
Create the example log file
$log = New-TemporaryFile
#'
<requestID>qwerty1-qwerty2-qwerty3</requestID> -match
<requestID>12345a-12345b-12345c</requestID>
<requestID>qwerty1-qwerty2-qwerty3</requestID>
<requestID>qwerty1-qwerty2-qwerty3</requestID>
<orderID>012345ABCDE</orderID>
<orderID>012345ABCDE</orderID>
<orderID>ABCDE012345</orderID>
<orderID>ABCDE012345</orderID>
<keyId>XYZ123</keyId>
<keyId>ABC987</keyId>
<keyId>XYZ123</keyId>
'# | Set-Content $log -Encoding UTF8
Define "tracker" variables for each type containing the count and a matches array
$Request = #{
Count = 1
Matches = #()
}
$Order = #{
Count = 1
Matches = #()
}
$Key = #{
Count = 1
Matches = #()
}
Read and process the log file line by line
$output = switch -Regex -File $log {
'<requestID>(\w{6,7}-\w{6,7}-\w{6,7})</requestID>' {
if(!$Request.matches.($matches.1))
{
$Request.matches += #{$matches.1 = "Request-$($Request.count)"}
$Request.count++
}
$_ -replace $matches.1,$Request.matches.($matches.1)
}
'<orderID>(\w{11})</orderID>' {
if(!$Order.matches.($matches.1))
{
$Order.matches += #{$matches.1 = "Order-$($Order.count)"}
$Order.count++
}
$_ -replace $matches.1,$Order.matches.($matches.1)
}
'<keyId>(\w{6})</keyId>' {
if(!$Key.matches.($matches.1))
{
$Key.matches += #{$matches.1 = "Key-$($Key.count)"}
$Key.count++
}
$_ -replace $matches.1,$Key.matches.($matches.1)
}
default {$_}
}
$output | Set-Content $log -Encoding UTF8
The $log file now contains
<requestID>Request-1</requestID>
<requestID>Request-2</requestID>
<requestID>Request-1</requestID>
<requestID>Request-1</requestID>
<orderID>Order-1</orderID>
<orderID>Order-1</orderID>
<orderID>Order-2</orderID>
<orderID>Order-2</orderID>
<keyId>Key-1</keyId>
<keyId>Key-2</keyId>
<keyId>Key-1</keyId>

Related

Powershell : Find a group of text in a file then extract a specific line in that group of text

I've been on this for few days now, I'm trying to parse multiple text files containing data like this :
[Cluster1]
GatewayIp=xx.xxx.xxx.xx
IpAddress=xx.xxx.xxx.x
MTU=0000
NetMask=xxx.xxx.xxx.0
Port=xxx
Protocol=xxxx/xxxxx
Sessions=xxxxxx
Bands=xxx, xxx, x
Binding=xxxxx
GroupNumber=x
InitQueue=xxxxxx
Interface=xxxxxx
Process=xxx
SupportsCar=No
SupportsCom=Yes
SupportsPos=Yes
SupportsXvd=No
[Cluster2]
GatewayIp=xx.xxx.xxx.xx
IpAddress=xx.xxx.xxx.x
MTU=0000
NetMask=xxx.xxx.xxx.0
Port=xxx
Protocol=xxxx/xxxxx
Sessions=xxxxxx
Bands=xxx, xxx, x
Binding=xxxxx
GroupNumber=x
InitQueue=xxxxxx
Interface=xxxxxx
Process=xxx
SupportsCar=No
SupportsCom=No
SupportsPos=No
SupportsXvd=Yes
I want to extract the "IpAddress" in the section where thoses lines are present :
SupportsCom=Yes
SupportsPos=Yes
The thing is, I've tried using -context to grab the nth line after the section name "[Cluster1]", but that section name is different from file to file ...
$ip = Select-String -Path "$location" -Pattern "\[Cluster1\]" -Context 0,2 |
Foreach-Object {$_.Context.PostContext}
I've tried using the Precontext to grab the Nth line before SupportsCom=Yes, but the line position of "IpAddress=" is different from file to file ...
$ip = Select-String -Path "$location" -Pattern " SupportsCom=Yes" -Context 14,0 |
Foreach-Object { $_.Line,$_.Context.PreContext[0].Trim()}
Is there a way to grab the section containing "SupportsCom=Yes" knowing that the section is delimited by a blank line above and below, then search in that section a string that contains "IpAddress=" then return the value afterthe "=" ?
Ok, since you are not allowed to use a module (perhaps later..), this should get you what you want
# change the extension in the Filter to match that of your files
$configFiles = Get-ChildItem -Path 'X:\somewhere' -Filter '*.ini' -File
$result = foreach ($file in $configFiles) {
# initialize these variables to $null
$IpAddress = $supportsCom = $supportsPos = $null
# loop through the file line by line and try regex matches on them
switch -Regex -File $file {
'^\[([^\]]+)]' {
# did we get all wanted entries from the previous cluster?
if ($IpAddress -and $supportsCom -and $supportsPos) {
if ($supportsCom -eq 'Yes' -and $supportsPos -eq 'Yes') {
# just output the IpAddress so it gets collected in variable $result
$IpAddress
}
# reset the variables to $null
$IpAddress = $supportsCom = $supportsPos = $null
}
# start a new cluster
$cluster = $matches[1]
}
'^\s+IpAddress\s*=\s*(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})' { $IpAddress = $matches[1]}
'^\s+SupportsCom\s*=\s*(Yes|No)' { $supportsCom = $matches[1] }
'^\s+SupportsPos\s*=\s*(Yes|No)' { $supportsPos = $matches[1]}
}
}
# show results on screen
$result
# or save as text file
$result | Set-Content -Path 'X:\somewhere\IpAddresses.txt'
Updated answer:
If you don't care about the name of the section(s), where IpAddress is found in, you can use this "one-liner" (broken into multiple lines for readability):
$ip = (Get-Content $location -Raw) -split '\[.+?\]' |
ConvertFrom-StringData |
Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } |
ForEach-Object IpAddress
The Get-Content line reads the input file as a single multi-line string and splits it at the section headers (e. g. [Cluster1]).
ConvertFrom-StringData converts the Key = Value lines into one hashtable per section.
For each hashtable, Where-Object checks whether it contains SupportsCom=Yes and SupportsPos=Yes
ForEach-Object IpAddress is shorthand for writing Select-Object -ExpandProperty IpAddress which gives you the actual value of IpAddress instead of an object that contains a member named IpAddress.
Note that $ip can be either a single string value or an array of strings (if there are multiple matching sections).
Original answer:
You could also write a general-purpose function that converts INI sections into objects. This enables you to use the pipeline with a simple Where-Object statement to get the data you are interested in.
Generic function to output INI sections as objects, one by one:
Function Read-IniObjects {
[CmdletBinding()]
param (
[Parameter(Mandatory, ValueFromPipeline)] [String] $Path
)
process {
$section = #{} # A hashtable that stores all properties of the currently processed INI section.
# Read file line by line and match each line by the given regular expressions.
switch -File $Path -RegEx {
'^\s*\[(.+?)\]\s*$' { # [SECTION]
# Output all data of previous section
if( $section.Count ) { [PSCustomObject] $section }
# Create new section data
$section = [ordered] #{ IniSection = $matches[ 1 ] }
}
'^\s*(.+?)\s*=\s*(.+?)\s*$' { # KEY = VALUE
$key, $value = $matches[ 1..2 ]
$section.$key = $value
}
}
# Output all data of last section
if( $section.Count ) { [PSCustomObject] $section }
}
}
Usage:
$ip = Read-IniObjects 'test.ini' |
Where-Object { $_.SupportsCom -eq 'Yes' -and $_.SupportsPos -eq 'Yes' } |
ForEach-Object IpAddress
Notes:
The INI file is parsed using the switch statement, which can directly use a file as input. This is much faster than using a Get-Content loop.
As we are using -RegEx parameter, the switch statement matches each line of the file to the given regular expressions, entering the case branches only if the current line matches.
Get detailed explanation about how the RegEx's work:
match lines like [Section] -> RegEx101
match lines like Key = Value -> RegEx101
ForEach-Object IpAddress is shorthand for writing Select-Object -ExpandProperty IpAddress which gives you the actual value of IpAddress instead of an object that contains a member named IpAddress.
Note that $ip can be either a single string value or an array of strings (if there are multiple matching sections).

Compare the contents of two files and output the the differences in contents along with line numbers

I came upon the problem where we need to compare contents two files a.txt and b.txt line by line and output the result if any difference found along with content and line number.
We should not use Compare-Object in this scenario. Do we have any alternative?
I tried using for loops but unable to get desired result
For ex : a.txt:
Hello = "Required"
World = 5678
Environment = "new"
Available = 9080.90
b.txt"
Hello = "Required"
World = 5678.908
Environment = "old"
Available = 6780.90
I need to get the output as:
Line number 2:World is not matching
Line number 3:Environment is not matching
Line number 4:Available is not matching
I tried with the following code snippet but was unsuccessful
$file1 = Get-Content "C:\Users\Desktop\a.txt"
$file2 = Get-Content "C:\Users\Desktop\b.txt"
$result = "C:\Users\Desktop\result.txt"
$file1 | foreach {
$match = $file2 -match $_
if ( $match ){
$match | Out-File -Force $result -Append
}
}
As you seem to have an adverse reaction to Compare-Object, lets try this extremely janky set-up. As you have little to no requirements listed, this will give you the bare minimum to meet your conditions of 'any difference found'.
Copy and paste more If statements should you have more lines.
$a = get-content C:\a.txt
$b = get-content C:\b.txt
If($a[0] -ne $b[0]) {
"Line number 1:Hello is not matching" | Out-Host
}
If($a[1] -ne $b[1]) {
"Line number 2:World is not matching" | Out-Host
}
If($a[2] -ne $b[2]) {
"Line number 3:Environment is not matching" | Out-Host
}
If($a[3] -ne $b[3]) {
"Line number 4:Available is not matching" | Out-Host
}
Get-Content returns the file content as an array of strings with a zero based index.
The array variable has an automatic property .Count/.Length
you can use to iterate the arrays with a simple counting for.
You need to split the line at the = to separate name and content.
Use -f format operator to output the results.
## Q:\Test\2019\05\21\SO_56231110.ps1
$Desktop = [environment]::GetFolderPath('Desktop')
$File1 = Get-Content (Join-Path $Desktop "a.txt")
$File2 = Get-Content (Join-Path $Desktop "b.txt")
for ($i=0;$i -lt $File.Count;$i++){
if($File1[$i] -ne $File2[$i]){
"Line number {0}:{1} is not matching" -f ($i+1),($File1[$i] -split ' = ')[0]
}
}
Sample output:
Line number 2:World is not matching
Line number 3:Environment is not matching
Line number 4:Available is not matching

In PowerShell how do I remove a duplicate item from a split path without changing the order?

I'm trying to remove duplicates from the Windows environment system path. It seems like the only way to do this is to split the path by semicolon and use sort-object with the -unique parameter or pipe to get-unique. However, it changes the sort order completely after that, and I want it to be the same order it was from the beginning, just minus the duplicates.
$RegPath = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
$hklm = [Microsoft.Win32.Registry]::LocalMachine
$RegKey = $hklm.OpenSubKey($regPath, $FALSE)
$OldPath = $regKey.GetValue("Path", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$SplitPath = $OldPath -split ';'
$NoDupesPath = ($SplitPath | Sort-Object | Get-Unique) -join ';'
I want $NoDupesPath to be the same order as $OldPath, minus the duplicates, but that's not happening when using Sort-Object. I also don't want any semicolons to show up at the very beginning or end of $NoDupesPath.
Inspire from this snippet I just threw together.
$s = 'a;b;c;b;b;a;a'
$a = $s -split ';'
$h = [ordered]#{}
# could use any value, or +1 to see count
$a | % {$h[$_] = $h[$_]+1}
"our hash table"
$h
"keys is unique and in order"
$h.Keys
$uniqueSameOrder = $h.Keys -join ';'
"uniqueSameOrder"
$uniqueSameOrder
Indeed, eliminating duplicates with Get-Unique (or, more directly, with Sort-Object -Unique) requires sorting the elements, which contradicts your requirements.
Update: Don Cruickshank's answer offers the simplest solution; conceptually speaking, the solution below is a manual (and therefore unnecessary) re-implementation of Select-Object -Unique.
Kory Gill's helpful answer contains all the ingredients for a solution; here's the full recipe:
$NoDupesPath = $OldPath -split ';' | ForEach-Object `
-Begin { $oht = [ordered] #{} } `
-Process { $oht[$_] = $true } `
-End { $oht.Keys -join ';' }
Due to pipeline use, this won't be the fastest solution, but it's concise and (hopefully) conceptually clear:
At the start of processing (the -Begin block) , $oht = [ordered] #{} creates an empty ordered hashtable (a hashtable whose keys reflect the order in which keys (entries) are created; PSv3+)
For each input path (the -Process block), if ($oht[$_]) { return } is a no-op if the input path at hand is already in the ordered hashtable, thereby skipping duplicates.
$oht[$_] = $true creates a hashtable entry for a path not previously seen; for a duplicate, the existing entry is simply updated, which amounts to a no-op, causing duplicates to be effectively ignored; after all paths have been processed, $oht.Keys therefore contains only unique paths, in input order.
At the end of processing (the -End block), $oht.Keys -join ';' outputs the unique paths - in input order - joined to form a single string with separator ;.
You can use Select-Object -Unique to get a unique collection and keep the order of the first occurrence of each item.
PS> $OldPath = "foo;bar;foo;baz;foo;bar;qux;baz;bar"
PS> $NoDupesPath = ($OldPath -split ';' | Select-Object -Unique) -join ';'
PS> $NoDupesPath
foo;bar;baz;qux
here's a solution that uses Group-Object. [grin] one thing that is not always apparent is that the resulting groups are in the order they are detected. that means you automatically get the original sequence in this situation ...
$OriSequence = 'a;f;c;b;b;f;a;a;c;s;r;v;q'
$SplitSequence = $OriSequence.Split(';')
$UniqueSequence = ($SplitSequence |
Group-Object).Name -join ';'
$OriSequence
$UniqueSequence
output ...
a;f;c;b;b;f;a;a;c;s;r;v;q
a;f;c;b;s;r;v;q

Read numbers from multiple files and sum

I have a logfile C:\temp\data.log
It contains the following data:
totalSize = 222,6GB
totalSize = 4,2GB
totalSize = 56,2GB
My goal is to extract the numbers from the file and sum them up including the number after the comma. So far it works if I don't regex the number included with value after comma, and only use the number in front of the comma. The other problem I have is if the file only contains one row like below example, if it only contains one line it splits up the number 222 into three file containing the number 2 in three files. If the above logfile contains 2 lines or more it works and sums up as it should, as long I don't use value with comma.
totalSize = 222,6GB
Here is a bit of the code for the regex to add to end of existing variable $regex included with comma is:
[,](\d{1,})
I haven't included the above regex, as it does not sum up properly then.
The whole script is below:
#Create path variable to store contents grabbed from $log_file
$extracted_strings = "C:\temp\amount.txt"
#Create path variable to read from original file
$log_file = "C:\temp\data.log"
#Read data from file $log_file
Get-Content -Path $log_file | Select-String "(totalSize = )" | out-file $extracted_strings
#Create path variable to write only numbers to file $output_numbers
$output_numbers = "C:\temp\amountresult.log"
#Create path variable to write to file jobblog1
$joblog1_file = "C:\temp\joblog1.txt"
#Create path variable to write to file jobblog2
$joblog2_file = "C:\temp\joblog2.txt"
#Create path variable to write to file jobblog3
$joblog3_file = "C:\temp\joblog3.txt"
#Create path variable to write to file jobblog4
$joblog4_file = "C:\temp\joblog4.txt"
#Create path variable to write to file jobblog5
$joblog5_file = "C:\temp\joblog5.txt"
#Create pattern variable to read with select string
$regex = "[= ](\d{1,})"
select-string -Path $extracted_strings -Pattern $regex -AllMatches | % { $_.Matches } | % { $_.Value } > $output_numbers
(Get-Content -Path $output_numbers)[0..0] -replace '\s' > $joblog1_file
(Get-Content -Path $output_numbers)[1..1] -replace '\s' > $joblog2_file
(Get-Content -Path $output_numbers)[2..2] -replace '\s' > $joblog3_file
(Get-Content -Path $output_numbers)[3..3] -replace '\s' > $joblog4_file
(Get-Content -Path $output_numbers)[4..4] -replace '\s' > $joblog5_file
$jobdata0 = (Get-Content -Path $joblog1_file)
$jobdata1 = (Get-Content -Path $joblog2_file)
$jobdata2 = (Get-Content -Path $joblog3_file)
$jobdata3 = (Get-Content -Path $joblog4_file)
$jobdata4 = (Get-Content -Path $joblog5_file)
$result = $jobdata0 + $jobdata1 + $jobdata2 + $jobdata3 + $jobdata4
$result
So my questions is:
How can I get this to work if the file C:\temp\data.log only contains one string without dividing that single number into multiple files. It should also work if it contains multiple strings, as it is now it works with multiple strings.
And how can I include the comma values in the calculation?
The result I get if I run this script should be 282, maybe its even possible to shorten the script?
Where $log_file has contents like the example above.
Get-Content $log_file | Where-Object{$_ -match "\d+(,\d+)?"} |
ForEach-Object{[double]($matches[0] -replace ",",".")} |
Measure-Object -Sum |
Select-Object -ExpandProperty sum
Match all of the lines that have numerical values with optional commas. I am assuming they could be optional as I do not know how whole numbers appear. Replace the comma with a period and cast as a double. Using measure object we sum up all the values and expand the result.
Not the only way to do it but it is simple enough to understand what is going on.
You can always wrap the above up in a loop so that you can use it for multiple files. Get-ChildItem "C:temp\" -Filter "job*" | ForEach-Object... etc.
Matt's helpful answer shows a concise and effective solution.
As for what you tried:
As for why a line with a single token such as 222,6 can result in multiple outputs in this command:
select-string -Path $extracted_strings -Pattern $regex -AllMatches |
% { $_.Matches } | % { $_.Value } > $output_numbers
Your regex, [= ](\d{1,}), does not explain the symptom, but just \d{1,} would, because that would capture 222 and 6 separately, due to -AllMatches.
[= ](\d{1,}) probably doesn't do what you want, because [= ] matches a single character that can be either a = or a space; with your sample input, this would only ever match the space before the numbers.
To match characters in sequence, simply place them next to each other: = (\d{1,})
Also note that even though you're enclosing \d{1,} in (...) to create a capture group, your later code doesn't actually use what that capture group matched; use (...) only if you need it for precedence (in which case you can even opt out of subexpression capturing with (?:...)) or if you do have a need to access what the subexpression matched.
That said, you could actually utilize a capture group here (an alternative would be to use a look-behind assertion), which allows you to both match the leading =<space> for robustness and extract only the numeric token of interest (saving you the need to trim whitespace later).
If we simplify \d{1,} to \d+ and append ,\d+ to also match the number after the comma, we get:
= (\d+,\d+)
The [System.Text.RegularExpressions.Match] instances returned by Select-String then allow us to access what the capture group captured, via the .Groups property (the following simplified example also works with multiple input lines):
> 'totalSize = 222,6GB' | Select-String '= (\d+,\d+)' | % { $_.Matches.Groups[1].Value }
222,6
On a side note: your code contains a lot of repetition that could be eliminated with arrays and pipelines; for instance:
$joblog1_file = "C:\temp\joblog1.txt"
$joblog2_file = "C:\temp\joblog2.txt"
$joblog3_file = "C:\temp\joblog3.txt"
$joblog4_file = "C:\temp\joblog4.txt"
$joblog5_file = "C:\temp\joblog5.txt"
could be replaced with (create an array of filenames, using a pipeline):
$joblog_files = 1..5 | % { "C:\temp\joblog$_.txt" }
and
$jobdata0 = (Get-Content -Path $joblog1_file)
$jobdata1 = (Get-Content -Path $joblog2_file)
$jobdata2 = (Get-Content -Path $joblog3_file)
$jobdata3 = (Get-Content -Path $joblog4_file)
$jobdata4 = (Get-Content -Path $joblog5_file)
$result = $jobdata0 + $jobdata1 + $jobdata2 + $jobdata3 + $jobdata4
could then be replaced with (pass the array of filenames to Get-Content):
$result = Get-Content $joblog_files

Retrieving second part of a line when first part matches exactly

I used the below steps to retrieve a string from file
$variable = 'abc#yahoo.com'
$test = $variable.split('#')[0];
$file = Get-Content C:\Temp\file1.txt | Where-Object { $_.Contains($test) }
$postPipePortion = $file | Foreach-Object {$_.Substring($_.IndexOf("|") + 1)}
This results in all lines that contain $test as a substring. I just want the result to contain only the lines that exactly matches $test.
For example, If a file contains
abc_def|hf#23$
abc|ohgvtre
I just want the text ohgvtre
If I understand the question correctly you probably want to use Import-Csv instead of Get-Content:
Import-Csv 'C:\Temp\file1.txt' -Delimiter '|' -Header 'foo', 'bar' |
Where-Object { $_.foo -eq $test } |
Select-Object -Expand bar
To address the exact matching, you should be testing for equality (-eq) rather than substring (.Contains()). Also, there is no need to parse the data multiple times. Here is your code, rewritten to to operate in one pass over the data using the -split operator.
$variable = 'abc#yahoo.com'
$test = $variable.split('#')[0];
$postPipePortion = (
# Iterate once over the lines in file1.txt
Get-Content C:\Temp\file1.txt | foreach {
# Split the string, keeping both parts in separate variables.
# Note the backslash - the argument to the -split operator is a regex
$first, $second = ($_ -split '\|')
# When the first half matches, output the second half.
if ($first -eq $test) {
$second
}
}
)