I receive a text file with a multiple lists like shown below (edit: more accurate example dataset included)
# SYSTEM X
# SINGULAR
192.168.1.3
# SUB-SYSTEM V
192.168.1.4
192.168.1.5
192.168.1.6
# SYSTEM Y
# MANDATORY
192.168.1.7
192.168.1.8
192.168.1.9
192.168.1.7
192.168.1.8
192.168.1.9
Each "SYSTEM comment" means its a new set after it.
I want to read each block of content separately so each set should be assigned to an object discarding the embedded comments. I just need the IPs.
Something like:
$ipX = get-content -path [file.txt] [set X]
$ipY = get-content -path [file.txt] [set Y]
$ipZ = get-content -path [file.txt] [set Z]
But I'm not sure how to actually assign these sets separately.
Help please.
Here's one possible solution. The result will be a hashtable, each key containing any array of ips for the set:
$result = #{}
get-content file.txt | foreach {
if ($_ -match "#\s*SET\s+(\w+)") {
$result[($key = $matches.1)] = #()
}
elseif ($_ -notlike "#*") {
$result[$key] += $_
}
}
Contents of $result:
Name Value
---- -----
Y {[ip], [ip], [more ips]}
Z {[ip], [ip], [more ips]}
X {[ip], [ip], [more ips]}
Here's another approach. We will take advantage of Foreach-Object's -End block to [PSCustomObject] the final one.
Get-Content $file | Foreach-Object {
if($_ -match 'SET (.+?)'){
if($ht){[PSCustomObject]$ht}
$ht = [ordered]#{Set = $Matches.1}
}
if($_ -match '^[^#]'){
$ht["IPs"] += $_
}
} -End {if($ht){[PSCustomObject]$ht}}
Output
Set IPs
--- ---
X [ip][ip][more ips]
Y [ip][ip][more ips]
Z [ip][ip][more ips]
If you want to also ensure $ht is empty to start with you could use the -Begin block.
Get-Content $file | Foreach-Object -Begin{$ht=$null}{
if($_ -match 'SET (.+?)'){
if($ht){[PSCustomObject]$ht}
$ht = [ordered]#{Set = $Matches.1}
}
if($_ -match '^[^#]'){
$ht["IPs"] += $_
}
} -End {if($ht){[PSCustomObject]$ht}}
You can use Select-String to extract a specific section of text:
# Update $section to be the set you want to target
$section = 'Set Y'
Get-Content a.txt -Raw |
Select-String -Pattern "# $section.*\r?\n(?s)(.*?)(?=\r?\n# Set|$)" | Foreach-Object
{$_.Matches.Groups[1].Value}
Using Get-Content with -Raw reads in the file as a single string making multi-line matching easier. With PowerShell 7, Select-String includes a -Raw switch making this process a bit simpler.
This outputs capture group 1 results, which match the (.*?). If you want to capture between comments rather than between Set <something> and Set <something>, you can edit the -Pattern value at the end to only be # rather than # Set.
Regex Breakdown:
# matches the characters # literally
$section substitutes your variable value matches the value literally provided there are no regex characters in the string
.* matches any character (except for line terminators)
\r matches a carriage return
? Quantifier — Matches between zero and one times, as many times as
possible, giving back as needed (greedy)
\n matches a line-feed (newline) character
(?s) modifier: single line. Dot matches newline characters
1st Capturing Group (.*?)
.*? matches any characters lazily
Positive Lookahead (?=\r?\n# Set)
\r? matches a carriage return zero or more times
\n matches a line-feed (newline) character
# Set matches the characters # Set literally
$ matches the end of the string
If I understand the question with the new example correctly, you want to parse out the file and create single variables of that each holding an array ip IP addresses.
If that is the case, you could do:
# loop through the file line-by-line
$result = switch -Regex -File 'D:\Test\thefile.txt' {
'#\sSYSTEM\s(\w+)' {
# start a new object, output the earlier object if available
if ($obj) { $obj }
$obj = [PsCustomObject]#{ 'System' = $Matches[1]; 'Ip' = #() }
}
'\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}' {
# looks like an IPv4 address. Add it to the Ip property array of the object
$obj.Ip += $_
}
default {}
}
Now you have an array ob objects in $result:
System Ip
------ --
Y {192.168.1.7, 192.168.1.8, 192.168.1.9, 192.168.1.7...}
X {192.168.1.3, 192.168.1.4, 192.168.1.5, 192.168.1.6}
To make separate variables of that is as easy as:
$ipX = ($result | Where-Object { $_.System -eq 'X' }).Ip
$ipY = ($result | Where-Object { $_.System -eq 'Y' }).Ip
$ipZ = ($result | Where-Object { $_.System -eq 'Z' }).Ip
Your example has duplicate IP addresses. If you don't want these do
$ipX = ($result | Where-Object { $_.System -eq 'X' }).Ip | Select-Object -Unique (same for the others)
Related
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).
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.
I want to be able to split some text out of a txtfile:
For example:
Brackets#Release 1.11.6#Path-to-Brackets
Atom#v1.4#Path-to-Atom
I just want to have the "Release 1.11.6" part. I am doing a where-object starts with Brackets but I don't know the full syntax. Here is my code:
"Get-Content -Path thisfile.txt | Where-Object{$_ < IM STUCK HERE > !
You could do this:
((Get-Content thisfile.txt | Where-Object { $_ -match '^Brackets' }) -Split '#')[1]
This uses the -match operator to filter out any lines that don't start with Brackets (the ^ special regex character indicates that what follows must be at the beginning of the line). Then it uses the -Split operator to split those lines on # and then it uses the array index [1] to get the second element of the split (arrays start at 0).
Note that this will throw an error if the split on # doesn't return at least two elements and it assumes that the text you want is always the second of those elements.
$bracketsRelease = Get-Content -path thisfile.txt | foreach-object {
if ( $_ -match 'Brackets#(Release [^#]+)#' )
{
$Matches[1]
}
}
or
(select-string -Path file.txt -Pattern 'Brackets#(Release [^#]+)#').Matches[0].Groups[1].value
I am new to Powershell scripting, but I feel I am overlooking a simple answer, hopefully some of you can help.
My company exports files from all of our computers with a section around the middle of Mapped Network Printers. It looks like this:
-------------------------------------------------------------------------
Mapped Network Printers:
NetworkAddress\HP425DN [DEFAULT PRINTER]
-------------------------------------------------------------------------
Local Printers:
What I have been asked to do is copy just the Mapped Network Printers to a new text file.
I tried using Select-String with a context parameter, but I have no way of knowing how many network printers there are, so I can't guess.
I also tried using the following code which I found on this site, but it returns nothing:
$MapPrint = gc C:\Users\User1\Documents\Config.txt
$from = ($MapPrint | Select-String -pattern "Mapped Network Printers:" |
Select-Object LineNumber).LineNumber
$to = ($MapPrint | Select-String -pattern "-------------------------------
--------------------------------------------" | Select-Object
LineNumber).LineNumber
$i = 0
$array = #()
foreach ($line in $MapPrint)
{
foreach-object { $i++ }
if (($i -gt $from) -and ($i -lt $to))
{
$array += $line
}
}
$array
I basically want to start the search at "Mapped Network Printers" and end it at the next row of "------"
Any help would be greatly appreciated.
Select-String has no feature for extracting a range of lines based on content.
The simplest approach is to read the file as a whole and use the -replace operator to extract the range via a regular expression (regex):
$file = 'C:\Users\User1\Documents\Config.txt'
$regex = '(?sm).*^Mapped Network Printers:\r?\n(.*?)\r?\n---------------------.*'
(Get-Content -Raw $file) -replace $regex, '$1'
Reading an input file as a whole can be problematic with files too large to fit into memory, but that's probably not a concern for you.
On the plus side, this approach is much faster than processing the lines in a loop.
Get-Content -Raw (PSv3+) reads the input file as a whole.
Inline regex options (?sm) turn on both the multi-line and the single-line option:
m means that ^ and $ match the start and end of each line rather than the input string as a whole.
s means that metacharacter . matches \n characters too, so that an expression such as .* can be used to match across lines.
\r?\n matches a single line break, both the CRLF and the LF variety.
(.*?) is the capture group that (non-greedily) captures everything between the bounding lines.
Note that the regex matches the entire input string, and then replaces it with just the substring (range) of interest, captured in the 1st (and only) capture group ($1).
Assuming that $file contains:
-------------------------------------------------------------------------
Mapped Network Printers:
NetworkAddress\HP425DN [DEFAULT PRINTER]
NetworkAddress\HP426DN
-------------------------------------------------------------------------
Local Printers:
the above yields:
NetworkAddress\HP425DN [DEFAULT PRINTER]
NetworkAddress\HP426DN
You could use Select-String or Where-Object to look for words with a \. Taking that even further you could look for just the server\printer values with a RegEx like this:
Get-Content C:\Users\User1\Documents\Config.txt -Raw |
Select-String '[A-Z0-9]+\\[A-Z0-9]+' -AllMatches |
ForEach-Object {$_.Matches.Value}
Note that this makes the assumption the Server Names and Printers use only A-Z and 0-9, you may need to look for more characters if that is not a valid assumption.
Here would be an example of using Where-Object to filter for lines with \
Get-Content 'C:\Users\User1\Documents\Config.txt' | Where-Object {$_ -like '*\*'}
$Doc= "C:\temp\test.txt"
$Doc_end ="C:\temp\testfiltered.txt"
$reader = [System.IO.File]::OpenText($Doc)
$cdata=""
while($null -ne ($line = $reader.ReadLine()))
{
if ($line -like ('---*') ) {$Read = 0 }
if ($Read -eq 1) {$cdata+= $line + "`r`n"}
if ($line -like ('Mapped Network Printers:*')) {$Read = 1}
}
$cdata | Out-File $Doc_end -Force
You can do what you are attempting with the foreach-object command and a few additional test conditions. Simply setting a flag when you encounter the Mapped Network Printers: line and then terminating output on the next line -like "---*" will work, e.g.
## positional parameters
param(
[Parameter(Mandatory=$true)][string]$infile
)
$beginprn = 0
get-content $infile | foreach-object {
# terminate condition
if ([int]$beginprn -eq 1 -and $_ -like "---*") {
break
}
# output Mapped printers
if ([int]$beginprn -eq 1) {
write-host $_
}
# begin condition
if ($_ -eq "Mapped Network Printers:") {
$beginprn = 1
}
}
Example Input File
-------------------------------------------------------------------------
Mapped Network Printers:
NetworkAddress\HP425DN [DEFAULT PRINTER]
NetworkAddress\HP4100N
-------------------------------------------------------------------------
Local Printers:
Example Use/Output
PS> parseprn.ps1 .\tmp\prnfile.txt
NetworkAddress\HP425DN [DEFAULT PRINTER]
NetworkAddress\HP4100N
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
}
}
)