Parse a list line by line, create a new list in Powershell - powershell

I need to read in a file that contains lines of source/destination IPs and ports as well as a tag. I'm using Get-Content:
Get-Content $logFile -ReadCount 1 | % {
} | sort | get-unique | Out-File "C:\Log\logout.txt"
This is an example of the input file:
|10.0.0.99|345|195.168.4.82|58164|spam|
|10.0.0.99|345|195.168.4.82|58164|robot|
|10.0.0.99|231|195.168.4.82|58162|spam|
|195.168.4.82|58162|10.0.0.99|231|robot|
|10.0.0.99|345|195.168.4.82|58168|spam|
|10.0.0.99|345|195.168.4.82|58169|spam|
What I need to do is output a new list, but if the same source/destination IPs/ports are both 'spam' and 'robot' I just need to output that line as 'robot' (lines 1 and 2 above).
I need to do the same if the reverse direction of an existing connection is either 'spam' or 'robot', I just need one or the other and it would be 'robot' (lines 3 and 4 above). There will be plenty of 'spam' lines without a duplicate or reverse connection (the last couple lines above), they need to just stay the same.
This is what i've been using to create the reverse direction of the connection, but I haven't been able to figure out how to properly create the new list:
$reverse = '|' + ($_.Split("|")[3,4,1,2,5] -join '|') + '|'
Output of the above would be:
|10.0.0.99|345|195.168.4.82|58164|robot|
|195.168.4.82|58162|10.0.0.99|231|robot|
|10.0.0.99|345|195.168.4.82|58168|spam|
|10.0.0.99|345|195.168.4.82|58169|spam|
(except that second line didn't have to be the reversed direction)
Thanks for any help!

Since both direct and reverse connections are checked and their line order may not be sequential, I would use a hashtable to store the type of both directions and do everything algorithmically:
$checkPoints = #{}
$output = [ordered]#{}
$reader = [IO.StreamReader]'R:\1.txt'
while (!$reader.EndOfStream) {
$line = $reader.ReadLine()
$s = $line.split('|')
$direct = [string]::Join('|', $s[1..4])
$reverse = [string]::Join('|', ($s[3,4,1,2]))
$type = $s[5]
$known = $checkPoints[$direct]
if (!$known -or ($type -eq 'robot' -and $known -eq 'spam')) {
$checkPoints[$direct] = $checkPoints[$reverse] = $type
$output[$direct] = $line
$output.Remove($reverse)
} elseif ($type -eq 'spam' -and $known -eq 'robot') {
$output.Remove($reverse)
}
}
$reader.Close()
Set-Content r:\2.txt -Encoding utf8 -value #($output.Values)

Related

Remove the need to use out-file only to import the file immediately using PowerShell just to convert the base type

I am attempting to turn the file below into one that contains no comments '#', no blank lines, no unneeded spaces, and only one entry per line. I'm unsure how to run the following code without the need to output the file and then reimport it. There should be code that doesn't require that step but I can't find it. The way I wrote my script also doesn't look right to me even though it works. As if there was a more elegant way of doing what I'm attempting but I just don't see it.
Before File Change: TNSNames.ora
#Created 9_27_16
#Updated 8_30_19
AAAA.world=(DESCRIPTION =(ADDRESS_LIST =
(ADDRESS =
(COMMUNITY = tcp.world)
(PROTOCOL = TCP)
(Host = www.url1111.com)
(Port = 1111)
)
)
(CONNECT_DATA = (SID = SID1111)
)
)
#Created 9_27_16
BBBB.world=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=url2222.COM)(Port=2222))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=url22222.COM)(Port=22222)))(CONNECT_DATA=(SID=SID2222)))
CCCC.world=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=url3333.COM)(Port=3333))(CONNECT_DATA=(SID=SID3333)))
DDDD.url =(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=URL4444 )(Port=4444))(ADDRESS=(COMMUNITY=TCP.world)(PROTOCOL=TCP)(Host=URL44444 )(Port=44444)))(CONNECT_DATA=(SID=SID4444 )(GLOBAL_NAME=ASDF.URL)))
#Created 9_27_16
#Updated 8_30_19
After File Change:
AAAA.world=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=www.url1111.com)(Port=1111)))(CONNECT_DATA=(SID=SID1111)))
BBBB.world=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=url2222.COM)(Port=2222))(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=url22222.COM)(Port=22222)))(CONNECT_DATA=(SID=SID2222)))
CCCC.world=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(Host=url3333.COM)(Port=3333))(CONNECT_DATA=(SID=SID3333)))
DDDD.url=(DESCRIPTION=(ADDRESS_LIST=(ADDRESS=(COMMUNITY=tcp.world)(PROTOCOL=TCP)(Host=URL4444)(Port=4444))(ADDRESS=(COMMUNITY=TCP.world)(PROTOCOL=TCP)(Host=URL44444)(Port=44444)))(CONNECT_DATA=(SID=SID4444)(GLOBAL_NAME=ASDF.URL)))
Code:
# Get the file
[System.IO.FileInfo] $File = 'C:\temp\TNSNames.ora'
[string] $data = (Get-Content $File.FullName | Where-Object { !$_.StartsWith('#') }).ToUpper()
# Convert the data. This part is where any (CONNECT_DATA entry ends up on it's own line.
$Results = $data.Replace(" ", "").Replace("`t", "").Replace(")))", ")))`n")
# Convert $Results from BaseType of System.Object to System.Array
$Path = '.\.vscode\StringResults.txt'
$Results | Out-File -FilePath $Path
$Results = Get-Content $Path
# Find all lines that start with '(CONNECT_DATA'
for ($i = 0; $i -lt $Results.Length - 1; $i++) {
if ($Results[$i + 1].StartsWith("(CONNECT_DATA")) {
# Add the '(CONNECT_DATA' line to the previous line
$Results[$i] = $Results[$i] + $Results[$i + 1]
# Blank out the '(CONNECT_DATA' line
$Results[$i + 1] = ''
}
}
# Remove all blank lines
$FinalForm = $null
foreach ($Line in $Results) {
if ($Line -ne "") {
$FinalForm += "$Line`n"
}
}
$FinalForm
So the crux of your problem is that you have declared $data as a [string] which is fine because probably some of your replace operations work better as a single string. Its just that $Results also then ends up being a string so when you try to index into $Results near the bottom these operations fail. You can however easily turn your $Results variable into a string array using the -split operator this would eliminate the need to save the string to disk and import back in just to accomplish the same. See comments below.
# Get the file
[System.IO.FileInfo] $File = 'C:\temp\TNSNames.ora'
[string] $data = (Get-Content $File.FullName | Where-Object { !$_.StartsWith('#') }).ToUpper()
# Convert the data. This part is where any (CONNECT_DATA entry ends up on it's own line.
$Results = $data.Replace(' ', '').Replace("`t", '').Replace(')))', ")))`n")
# You do not need to do this next section. Essentially this is just saving your multiline string
# to a file and then using Get-Content to read it back in as a string array
# Convert $Results from BaseType of System.Object to System.Array
# $Path = 'c:\temp\StringResults.txt'
# $Results | Out-File -FilePath $Path
# $Results = Get-Content $Path
# Instead split your $Results string into multiple lines using -split
# this will do the same thing as above without writing to file
$Results = $Results -split "\r?\n"
# Find all lines that start with '(CONNECT_DATA'
for ($i = 0; $i -lt $Results.Length - 1; $i++) {
if ($Results[$i + 1].StartsWith('(CONNECT_DATA')) {
# Add the '(CONNECT_DATA' line to the previous line
$Results[$i] = $Results[$i] + $Results[$i + 1]
# Blank out the '(CONNECT_DATA' line
$Results[$i + 1] = ''
}
}
# Remove all blank lines
$FinalForm = $null
foreach ($Line in $Results) {
if ($Line -ne '') {
$FinalForm += "$Line`n"
}
}
$FinalForm
Also, for fun, try this out
((Get-Content 'C:\temp\tnsnames.ora' |
Where-Object {!$_.StartsWith('#') -and ![string]::IsNullOrWhiteSpace($_)}) -join '' -replace '\s' -replace '\)\s?\)\s?\)', ")))`n" -replace '\r?\n\(Connect_data','(connect_data').ToUpper()

Getting Netstat output - code fails on Server 2008 R2

I am quite new to PowerShell, and scripting in general. I have been asked to generate a list of all listening TCP ports on a large set of servers over a period of time, returning a big csv file that can be imported and searched. Unfortunately some of them are still running Server 2008R2 (yeah, yeah I know...) so using Get-NetTCPConnection is out of the question. I pretty much have to try and run NetStat and make use of the output from that. I found a wonderful script written by Adam Bertram in 2015 called Get-LocalPort.ps1 which coverts the output to proper Powershell objects and appears to be ideal, however it doesn't run on Server 2008R2 either. It produces the error Method invocation failed because [System.Object[]] doesn't contain a method named 'Trim'. which I believe is coming from the line $Netstat = (netstat -anb | where {$_ -and ($_ -ne 'Active Connections')}).Trim() | Select-Object -Skip 1 | foreach {$_ -replace '\s{2,}','|'} I don't understand why that line works on newer versions but not on 2008R2. Can anyone help me tweak this so it runs on older versions of Powershell? Thanks very much.
The whole script is as follows:
<#
.SYNOPSIS
This parses the native netstat.exe's output using the command line "netstat -anb" to find
all of the network ports in use on a local machine and all associated processes and services
.NOTES
Created on: 2/15/2015
Created by: Adam Bertram
Filename: Get-LocalPort.ps1
.EXAMPLE
PS> Get-LocalPort.ps1
This example will find all network ports in uses on the local computer with associated
processes and services
.EXAMPLE
PS> Get-LocalPort.ps1 | Where-Object {$_.ProcessOwner -eq 'svchost.exe'}
This example will find all network ports in use on the local computer that were opened
by the svchost.exe process.
.EXAMPLE
PS> Get-LocalPort.ps1 | Where-Object {$_.IPVersion -eq 'IPv4'}
This example will find all network ports in use on the local computer using IPv4 only.
#>
[CmdletBinding()]
param ()
begin {
Set-StrictMode -Version Latest
$ErrorActionPreference = 'Stop'
}
process {
try {
## Capture the output of the native netstat.exe utility
## Remove the top row from the result and trim off any leading or trailing spaces from each line
## Replace all instances of more than 1 space with a pipe symbol. This allows easier parsing of
## the fields
$Netstat = (netstat -anb | where {$_ -and ($_ -ne 'Active Connections')}).Trim() | Select-Object -Skip 1 | foreach {$_ -replace '\s{2,}','|'}
$i = 0
foreach ($Line in $Netstat) {
## Create the hashtable to conver to object later
$Out = #{
'Protocol' = ''
'State' = ''
'IPVersion' = ''
'LocalAddress' = ''
'LocalPort' = ''
'RemoteAddress' = ''
'RemotePort' = ''
'ProcessOwner' = ''
'Service' = ''
}
## If the line is a port
if ($Line -cmatch '^[A-Z]{3}\|') {
$Cols = $Line.Split('|')
$Out.Protocol = $Cols[0]
## Some ports don't have a state. If they do, there's always 4 fields in the line
if ($Cols.Count -eq 4) {
$Out.State = $Cols[3]
}
## All port lines that start with a [ are IPv6
if ($Cols[1].StartsWith('[')) {
$Out.IPVersion = 'IPv6'
$Out.LocalAddress = $Cols[1].Split(']')[0].TrimStart('[')
$Out.LocalPort = $Cols[1].Split(']')[1].TrimStart(':')
if ($Cols[2] -eq '*:*') {
$Out.RemoteAddress = '*'
$Out.RemotePort = '*'
} else {
$Out.RemoteAddress = $Cols[2].Split(']')[0].TrimStart('[')
$Out.RemotePort = $Cols[2].Split(']')[1].TrimStart(':')
}
} else {
$Out.IPVersion = 'IPv4'
$Out.LocalAddress = $Cols[1].Split(':')[0]
$Out.LocalPort = $Cols[1].Split(':')[1]
$Out.RemoteAddress = $Cols[2].Split(':')[0]
$Out.RemotePort = $Cols[2].Split(':')[1]
}
## Because the process owner and service are on separate lines than the port line and the number of lines between them is variable
## this craziness was necessary. This line starts parsing the netstat output at the current port line and searches for all
## lines after that that are NOT a port line and finds the first one. This is how many lines there are until the next port
## is defined.
$LinesUntilNextPortNum = ($Netstat | Select-Object -Skip $i | Select-String -Pattern '^[A-Z]{3}\|' -NotMatch | Select-Object -First 1).LineNumber
## Add the current line to the number of lines until the next port definition to find the associated process owner and service name
$NextPortLineNum = $i + $LinesUntilNextPortNum
## This would contain the process owner and service name
$PortAttribs = $Netstat[($i+1)..$NextPortLineNum]
## The process owner is always enclosed in brackets of, if it can't find the owner, starts with 'Can'
$Out.ProcessOwner = $PortAttribs -match '^\[.*\.exe\]|Can'
if ($Out.ProcessOwner) {
## Get rid of the brackets and pick the first index because this is an array
$Out.ProcessOwner = ($Out.ProcessOwner -replace '\[|\]','')[0]
}
## A service is always a combination of multiple word characters at the start of the line
if ($PortAttribs -match '^\w+$') {
$Out.Service = ($PortAttribs -match '^\w+$')[0]
}
[pscustomobject]$Out
}
## Keep the counter
$i++
}
} catch {
Write-Error "Error: $($_.Exception.Message) - Line Number: $($_.InvocationInfo.ScriptLineNumber)"
}
}
You may do the following:
# skipping header
$ns = netstat -anb | Select -Skip 3
$ns | Foreach-Object {
# Trim surrounding spaces
$line = $_.Trim()
# Check for lines starting with TCP
if ($line -cmatch '^TCP') {
# Split lines by spaces
$p,$l,$f,$s = $line -split '\s+'
# Output $obj if it already exists before new one is created
if ($obj) { $obj }
# service and process owner are blanked since they are on another line
$obj = new-object -TypeName Psobject -Property #{
Protocol=$p
State=$s
IPVersion=('IPv6','IPv4')[$l -match ':.*:']
LocalAddress=($l -replace '[\[\]]|:[^:]+$')
LocalPort=$l -replace '^.*:'
RemoteAddress=($f -replace '[\[\]]|:[^:]+$')
RemotePort=$f -replace '^.*:'
ProcessOwner=''
Service=''
}
}
elseif ($line -cmatch '^UDP') {
$p,$l,$f = $line -split '\s+'
if ($obj) { $obj }
$obj = new-object -TypeName Psobject -Property #{
Protocol=$p
State=''
IPVersion=('IPv4','IPv6')[$l -match ':.*:']
LocalAddress=($l -replace '[\[\]]|:[^:]+$')
LocalPort=$l -replace '^.*:'
RemoteAddress=($f -replace '[\[\]]|:[^:]+$')
RemotePort=$f -replace '^.*:'
ProcessOwner=''
Service=''
}
}
# line starts with [ then it is service name
elseif ($line -match '^\[') {
$obj.Service = $line -replace '[\[\]]'
}
else {
$obj.ProcessOwner = $line
}
}
Thanks AdminOfThings your first comment was on the right track. The line $Netstat = netstat -anb | where {$_ -and ($_ -ne 'Active Connections')} | foreach { $_.Trim() } | Select-Object -Skip 1 | foreach {$_ -replace '\s{2,}','|'}' works however I also had to change the line at the end from [pscustomobject]$Out to New-Object -TypeName PSObject -Property $Out because [pscustomobject] apparently was another new thing in PS 3. With these changes it works on Server 2008R2, 2012R2, and 2016.

Changing multiple lines in a text file based on a psobject

I'm working on a script which will add some additional informations to a txt file. These informations are stored in a CSV file which looks like this (the data will differs each time the script will launch):
Number;A;B;ValueOfB
FP01340/05/20;0;1;GTU_01,GTU_03
FP01342/05/20;1;0;GTU01
The txt file looks like this (data inside will of course differ each time):
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere|||||
2|zwol|9,00|9,00|0,00
2|23|157,91|194,23|36,32
1|1|FP01341/05/20|2020-05-02|2020-05-02|2020-05-02|12,19|14,99|2,80|Some info |2222222|blabla|11-111 something||||
2|23|12,19|14,99|2,80
1|1|FP01342/05/20|2020-05-02|2020-05-02|2020-05-02|525,36|589,64|64,28|bla|222222|blba 36||62030|something||
2|5|213,93|224,63|10,70
2|8|120,34|129,97|9,63
2|23|191,09|235,04|43,95
What I need to do is to find a line which contains 'Number' and then add value 'A' and 'B' from a CSV in a form: |0|1 and then on the first line below, at the end, add 'ValueofB' in a form |AAA_01,AAA_03
So the first two lines should look like this at the end:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere||||||0|1
2|zwol|9,00|9,00|0,00|AAA_01,AAA_03
2|23|157,91|194,23|36,32
Rest of lines should not be touched.
I made a script which uses select-string method with context to find what I need to - put that into an object and then add to previously found strings what I need to and put that in to an another object.
My script is as follws:
$csvFile = Import-Csv -Path Somepath\file.csv -Delimiter ";"
$file = "Somepath2\SomeName.txt"
$LinesToChange = #()
$script:LinesToChange = $LinesToChange
$LinesOriginal = #()
$script:LinesOriginal = $LinesOriginal
foreach ($line in $csvFile) {
Select-String -Path $file -Pattern "$($Line.number)" -Encoding default -Context 0, 1 | ForEach-Object {
$1 = $_.Line
$2 = $_.Context.PostContext
}
$ListOrg = [pscustomobject]#{
Line_org = $1
Line_GTU_org = $2
}
$LinesOriginal = $LinesOriginal + $ListOrg
$lineNew = $ListOrg.Line_org | foreach { $_ + "|$($line.A)|$($line.B)" }
$GTUNew = $ListOrg.Line_GTU_org | foreach { $_ + "|$($line.ValueofB)" }
$ListNew = [pscustomobject]#{
Line_new = $lineNew
Line_GTU_new = $GTUNew
Line_org = $ListOrg.Line_org
Line_GTU_org = $ListOrg.Line_GTU_org
}
$LinesToChange = $LinesToChange + $ListNew
}
The output is an object $LinesToChange which have original lines and lines after the change. The issue is I have no idea how to use that to change the txt file. I tried few methods and ended up with file which contains updated lines but all others are doubbled (I tried foreach) or PS is using whole RAM and couldn't finish the job :)
My latest idea is to use something like that:
(Get-Content -Path $file) | ForEach-Object {
$line = $_
$LinesToChange.GetEnumerator() | ForEach-Object {
if ($line -match "$($LinesToChange.Line_org)") {
$line = $line -replace "$($LinesToChange.Line_org)", "$($LinesToChange.Line_new)"
}
if ($line -match "$($LinesToChange.Line_GTU_org)") {
$line = $line -replace "$($LinesToChange.Line_GTU_org)", "$($LinesToChange.Line_GTU_new)"
}
}
} | Set-Content -Path Somehere\newfile.txt
It seemed promising at first, but the variable $line contains all lines and as such it can't find the match.
Also I need to be sure that the second line will be directly below the first one (it is unlikely but it can be a case that there will be two or more lines with the same data while the "number" from CSV file is unique) so preferably while changing the txt file it would be needed to find a match for a two-liner; in short:
find this two lines:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere|||||
2|zwol|9,00|9,00|0,00
change them to:
1|1|FP01340/05/20|2020-05-02|2020-05-02|2020-05-02|166,91|203,23|36,32|nothing interesting 18|33333|63-111 somewhere||||||0|1
2|zwol|9,00|9,00|0,00|AAA_01,AAA_03
Do that for all lines in a $LinesToChange
Any help will be much appreciated!
Greetings!
Some strange text file you have there, but anyway, this should do it:
# read in the text file as string array
$txt = Get-Content -Path '<PathToTheTextFile>'
$csv = Import-Csv -Path '<PathToTheCSVFile>' -Delimiter ';'
# loop through the items (rows) in the CSV and find matching lines in the text array
foreach ($item in $csv) {
$match = $txt | Select-String -Pattern ('|{0}|' -f $item.Number) -SimpleMatch
if ($match) {
# update the matching text line (array indices count from 0, so we do -1)
$txt[$match.LineNumber -1] += ('|{0}|{1}' -f $item.A, $item.B)
# update the line following
$txt[$match.LineNumber] += ('|{0}' -f $item.ValueOfB)
}
}
# show updated text on screen
$txt
# save updated text to file
$txt | Set-Content -Path 'Somehere\newfile.txt'

How can I use PowerShell to import a file, and replace all existing placeholder variables with ones defined in the script? [duplicate]

This question already has answers here:
Expanding variables in file contents
(3 answers)
Closed 3 years ago.
I have a number of .txt files that I use as standardized templates for Arista switch deployments, but I want to update them rapidly using PowerShell.
I replaced all the necessary values in these templates with $variables and I now am attempting to write a script to replace them.
I found a solution that I liked, but it is not working for me. I am not sure what I am doing wrong here.
https://stackoverflow.com/a/9326779/
This is a snippet from the source file :
router bgp $asn
router-id 10.1.1.1
bgp listen range 192.168.$id.0/25 peer-group cluster$id remote-as $asn
neighbor cluster$id peer-group
neighbor cluster$id update-source Loopback0
neighbor cluster$id description cluster$id-BGP
neighbor cluster$id ebgp-multihop 3
neighbor cluster$id maximum-routes 12000
network 10.1.1.1/32
exit
Here is a snippet from the powershell script :
$newvars = #{
'$id' = '101'
'$asn' = '12345'
}
$template = '.\Arista\arista.txt'
$destination_file = '.\switchconfig' + $id + '.txt'
Get-Content -Path $template | ForEach-Object {
$line = $_
$newvars.GetEnumerator() | ForEach-Object {
if ($line -match $_.Key)
{
$line = $line -replace $_.Key, $_.Value
}
}
$line
} | Set-Content -Path $destination_file
What I want is to have a group of variables defined (upwards of 30), and then replace each instance of that variable in the text file with the value contained in the script.
This solution seemed good, since it would avoid doing a "replace" over and over, but it just prints the file as it originally was.
Since powershell uses $ as an identifier for variables (reserved), you have to properly escape that when running your method. Following is a little off but does what you are looking for. update your dictionary with \ before your $ sign to replace text including $.
$newvars = #{
'\$id' = '101'
'\$asn' = '12345'
}
$template = "C:\temp\new.txt"
$destination_file = "C:\temp\replaced.txt"
$data = #()
foreach($line in Get-Content $template) {
foreach($key in $newvars.Keys) {
if ($line -match $key) {
$line = $line -replace $key, $newvars[$key]
}
}
$data += $line
}
$data | Out-File $destination_file
Another thing to note.. in your file you are defining $id as the name of the file. I am not sure where but that variable would always be null as its not defined yet (unless your snippet here is different from your actual code.
If you want to use Invoke-Expression, you can use it in the following way,
$id = '101'
$asn = '12345'
$template = (Get-Content "C:\temp\new.txt") | out-string
$data = Invoke-Expression "`"$template`""
$data | Out-File "C:\Temp\test.txt"
You'll have to make sure your variables ($id, $asn) have a value to replace when evaluating the variables within your text file.

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
}
}
)