Powershell: specify email:password with random data in between - powershell

Similar like this - Extract email:password
However we have here the situation that in some files there is other data between the data I want to parse, as example:
email:lastname:firstname:password or email:lastname:firstname:dob:password
So my question is - with which command would I be able to ignore 2 segments like "lastname:firstname" or even 3 parts "lastname:firstname:dob". I am using the below regex to retrieve email:password from a big list.
$sw = [System.IO.StreamWriter]::new("$PWD/out.txt")
switch -regex -file in.txt {
'(?<=:)[^#:]+#[^:]+:.*' { $sw.WriteLine($Matches[0]) }
}
$sw.Close()

You need to refine your regex:
# Create sample input file
#'
...:foo#example.org:password1
...:bar#example.org:lastname:firstname:password2
...:baz#example.org:lastname:firstname:dob:password3
'# > in.txt
# Process the file line by line.
switch -regex -file in.txt {
'(?<=:)([^#:]+#[^:]+)(?:.*):(.*)' { '{0}:{1}' -f $Matches[1], $Matches[2] }
}
For brevity, saving the output to a file was omitted above, so the email-password pairs extracted print to the screen by default, namely as:
foo#example.org:password1
bar#example.org:password2
baz#example.org:password3
Explanation of the regex:
(?<=:) is a positive lookbehind assertion for ensuring that matching starts right after a : character.
Note: I based this requirement on your original question and its sample data.
([^#:]+#[^:]+) uses a capture group (capturing subexpression, (...)) to match an email address up to but not including the next :.
(?:.*): uses a non-capturing subexpression ((?:...)) that matches zero or more characters (.*) unconditionally followed by a :
(.*) uses a capture group to capture all remaining characters after what is effectively the last : on each line, assumed to be the password.
$Matches[1] and $Matches[2] refer to the 1st and 2nd capture-group matches, i.e. the email address and the password.

Assuming you had data like this:
"lastname:firstname"
"lastname:firstname:dob"
"lastname:firstname:password:somepassword"
"lastname:john:firstname:jacob:password:dingleheimershmit
You can move through each row like this:
$items = gc .\stack.txt
ForEach($item in $items){
}
Then we can split each row on a : character and check each of those to see if its a match for the string passwrod. If it is, then we check the next token in the row which should be a password.
This code will get you going, you'll just need to do something meaningful with $password.
$items = gc .\stack.txt
ForEach($item in $items){
"processing $item"
$tokens = $item.Split(":")
For($x=0; $x -lt $tokens.Count;$x++){
$token = $tokens[$x]
#"Checking if $token is like password"
if ($token -match "password"){
"since this token is like password, checking next token which should be a password"
$password = $tokens[$x+1]
Write-Host -ForegroundColor Yellow $password
}
}
}

Related

PowerShell script that searches for a string in a .txt and if it finds it, looks for the next line containing another string and does a job with it

I have the line
Select-String -Path ".\*.txt" -Pattern "6,16" -Context 20 | Select-Object -First 1
that would return 20 lines of context looking for a pattern of "6,16".
I need to look for the next line containing the string "ID number:" after the line of "6,16", read what is the text right next to "ID number:", find if this exact text exists in another "export.txt" file located in the same folder (so in ".\export.txt"), and see if it contains "6,16" on the same line as the one containing the text in question.
I know it may seem confusing, but what I mean is for example:
example.txt:5218: ID number:0002743284
shows whether this is true:
export.txt:9783: 0002743284 *some text on the same line for example* 6,16
If I understand the question correctly, you're looking for something like:
Select-String -List -Path *.txt -Pattern '\b6,16\b' -Context 0, 20 |
ForEach-Object {
if ($_.Context.PostContext -join "`n" -match '\bID number:(\d+)') {
Select-String -List -LiteralPath export.txt -Pattern "$($Matches[1]).+$($_.Pattern)"
}
}
Select-String's -List switch limits the matching to one match per input file; -Context 0,20 also includes the 20 lines following the matching one in the output (but none (0) before).
Note that I've placed \b, a word-boundary assertion at either end of the search pattern, 6,16, to rule out accidental false positives such as 96,169.
$_.Context.PostContext contains the array of lines following the matching line (which itself is stored in $_.Line):
-join "`n" joins them into a multi-line string, so as to ensure that the subsequent -match operation reports the captured results in the automatic $Matches variable, notably reporting the ID number of interest in $Matches[1], the text captured by the first (and only) capture group ((\d+)).
The captured ID is then used in combination with the original search pattern to form a regex that looks for both on the same line, and is passed to a second Select-String call that searches through export.txt
Note: An object representing the matching line, if any, is output by default; to return just $true or $false, replace -List with -Quiet.
There's a lot wrong with what you're expecting and the code you've tried so let's break it down and get to the solution. Kudos for attempting this on your own. First, here's the solution, read below this code for an explanation of what you were doing wrong and how to arrive at the code I've written:
# Get matching lines plus the following line from the example.txt seed file
$seedMatches = Select-String -Path .\example.txt -Pattern "6,\s*16" -Context 0, 2
# Obtain the ID number from the line following each match
$idNumbers = foreach( $match in $seedMatches ) {
$postMatchFields = $match.Context.PostContext -split ":\s*"
# Note: .IndexOf(object) is case-sensitive when looking for strings
# Returns -1 if not found
$idFieldIndex = $postMatchFields.IndexOf("ID number")
# Return the "ID number" to `$idNumbers` if "ID number" is found in $postMatchFields
if( $idFieldIndex -gt -1 ) {
$postMatchFields[$idFieldIndex + 1]
}
}
# Match lines in export.txt where both the $id and "6,16" appear
$exportMatches = foreach( $id in $idNumbers ) {
Select-String -Path .\export.txt -Pattern "^(?=.*\b$id\b)(?=.*\b6,\s*16\b).*$"
}
mklement0's answer essentially condenses this into less code, but I wanted to break this down fully.
First, Select-String -Path ".\*.txt" will look in all .txt files in the current directory. You'll want to narrow that down to a specific naming pattern you're looking for in the seed file (the file we want to find the ID to look for in the other files). For this example, I'll use example.txt and export.txt for the paths which you've used elsewhere in your question, without using globbing to match on filenames.
Next, -Context gives context of the surrounding lines from the match. You only care about the next line match so 0, 1 should suffice for -Context (0 lines before, 1 line after the match).
Finally, I've added \s* to the -Pattern to match on whitespace, should the 16 ever be padded from the ,. So now we have our Select-String command ready to go:
$seedMatches = Select-String -Path .\example.txt -Pattern "6,\s*16" -Context 0, 2
Next, we will need to loop over the matching results from the seed file. You can use foreach or ForEach-Object, but I'll use foreach in the example below.
For each $match in $seedMatches we'll need to get the $idNumbers from the lines following each match. When $match is ToString()'d, it will spit out the matched line and any surrounding context lines. Since we only have one line following the match for our context, we can grab $match.Context.PostContext for this.
Now we can get the $idNumber. We can split example.txt:5218: ID number:0002743284 into an array of strings by using the -split operator to split the string on the :\s* pattern (\s* matches on any or no whitespace). Once we have this, we can get the index of "ID Number" and get the value of the field immediately following it. Now we have our $idNumbers. I'll also add some protection below to ensure the ID numbers field is actually found before continuing.
$idNumbers = foreach( $match in $seedMatches ) {
$postMatchFields = $match.Context.PostContext -split ":\s*"
# Note: .IndexOf(object) is case-sensitive when looking for strings
# Returns -1 if not found
$idFieldIndex = $postMatchFields.IndexOf("ID number")
# Return the "ID number" to `$idNumbers` if "ID number" is found in $postMatchFields
if( $idFieldIndex -gt -1 ) {
$postMatchFields[$idFieldIndex + 1]
}
}
Now that we have $idNumbers, we can look in export.txt for this ID number "6,\s*16" on the same line, once again using Select-String. This time, I'll put the code first since it's nothing new, then explain the regex a bit:
$exportMatches = foreach( $id in $idNumbers ) {
Select-String -Path .\export.txt -Pattern "^(?=.*\b$id\b)(?=.*\b6,\s*16\b).*$"
}
$exportMatches will now contain the lines which contain both the target ID number and the 6,16 value on the same line. Note that order wasn't specified so the expression uses positive lookaheads to find both the $id and 6,16 values regardless of their order in the string. I won't break down the exact expression but if you plug ^(?=.*\b0123456789\b)(?=.*\b6,\s*16\b).*$ into https://regexr.com it will break down and explain the regex pattern in detail.
The full code is above in at the top of this answer.

Comparing multiple email address using powershell match

I have a CSV file of 2000 email addresses. I am using PowerShell to check if the user is active in AD. Another developer wrote a PowerShell script for me to do this but he only used the main domain for the email format to match, he didn't add the subdomian that it could have. Some of our email addresses have a 3 part email address after the # symbol.
For example, his code:
foreach ($user in $users) {
Write-Host $user.email
if ($user.email -match "\#mycompany\.com$") {
$status = "NOT FOUND"
# loop through possible AD domains until there is a hit
foreach ($domain in "na","au","eu","as") {
if ($status -eq "NOT FOUND") {
Write-Host " $($domain)" -NoNewline
$status = Get-UserFromEmail -EMail $user.email -ADDomain $domain
Write-Host $status
}
else {
break
}
}
Write-Host
Add-Content -Path $outcsv -Value "$($user.email),$($user.type),`"$($status)`""
}
else {
Add-Content -Path $outcsv -Value "$($user.email),$($user.type),NOT MYCOMPANY"
}
What I need to be able to do is get the match to check if it is a two or three part email address.
#consultant.mycompany.com or #mycompany.com.
Any insight for this PowerShell newbie would be appreciated.
here is one way to test for membership in more than one email domain. all of the domains are all in the same example.com, but they could easily be in testing.com or wizbang.org.
this demos the idea, i presume you can insert it into your script as needed. [grin]
what it does ...
builds a list of email addresses to test
you will get that from your source ... just be sure they are plain strings, not a string inside a property of an object.
builds a domain list
uses the built in regex escape method to escape things like dots as they are needed
adds a $ to the end of each escaped string to anchor the pattern to the end of the email address
uses the escaped strings to build a regex OR of that list
iterates thru the email address list and gets the ones that match one of the domain list items
saves the matches to a $Var
displays the content of that $Var on screen
the code ...
$EmailList = #(
'ABravo#example.com'
'BCharlie#more.example.com'
'CDelta#example.com'
'DEcho#zigzag.papers.com'
'EFoxtrot#even.more.example.com'
)
$DomainList = #(
'#example.com'
'#more.example.com'
'#even.more.example.com'
)
$Regex_DL = $DomainList.ForEach({
[regex]::Escape($_) + '$'
}) -join '|'
$ValidEmailAddressList = $EmailList -match $Regex_DL
$ValidEmailAddressList
output ...
ABravo#example.com
BCharlie#more.example.com
CDelta#example.com
EFoxtrot#even.more.example.com
You can always use the -or operator to chain multiple expressions inside the if condition:
if ($user.email -match "\#mycompany\.com$" -or $user.email -match '#consultant\.mycompany\.com$'){
# ...
}
Alternatively, you can construct a regex pattern that'll match both:
if($user.email -match '#(?:consultant\.)?mycompany\.com$'){
# ...
}
If you're ever unsure about how to escape a literal string in a reguar expression, use [regex]::Escape():
PS C:\> [regex]::Escape('#consultant.mycompany.com')
#consultant\.mycompany\.com

Reading strings from text files using switch -regex returns null element

Question:
The intention of my script is to filter out the name and phone number from both text files and add them into a hash table with the name being the key and the phone number being the value.
The problem I am facing is
$name = $_.Current is returning $null, as a result of which my hash is not getting populated.
Can someone tell me what the issue is?
Contents of File1.txt:
Lori
234 east 2nd street
Raleigh nc 12345
9199617621
lori#hotmail.com
=================
Contents of File2.txt:
Robert
2531 10th Avenue
Seattle WA 93413
2068869421
robert#hotmail.com
Sample Code:
$hash = #{}
Switch -regex (Get-content -Path C:\Users\svats\Desktop\Fil*.txt)
{
'^[a-z]+$' { $name = $_.current}
'^\d{10}' {
$phone = $_.current
$hash.Add($name,$phone)
$name=$phone=$null
}
default
{
write-host "Nothing matched"
}
}
$hash
Remove the current property reference from $_:
$hash = #{}
Switch -regex (Get-content -Path C:\Users\svats\Desktop\Fil*.txt)
{
'^[a-z]+$' {
$name = $_
}
'^\d{10}' {
$phone = $_
$hash.Add($name, $phone)
$name = $phone = $null
}
default {
Write-Host "Nothing matched"
}
}
$hash
Mathias R. Jessen's helpful answer explains your problem and offers an effective solution:
it is automatic variable $_ / $PSItem itself that contains the current input object (whatever its type is - what properties $_ / $PSItem has therefore depends on the input object's specific type).
Aside from that, there's potential for making the code both less verbose and more efficient:
# Initialize the output hashtable.
$hash = #{}
# Create the regex that will be used on each input file's content.
# (?...) sets options: i ... case-insensitive; m ... ^ and $ match
# the beginning and end of every *line*.
$re = [regex] '(?im)^([a-z]+|\d{10})$'
# Loop over each input file's content (as a whole, thanks to -Raw).
Get-Content -Raw File*.txt | foreach {
# Look for name and phone number.
$matchColl = $re.Matches($_)
if ($matchColl.Count -eq 2) { # Both found, add hashtable entry.
$hash.Add($matchColl.Value[0], $matchColl.Value[1])
} else {
Write-Host "Nothing matched."
}
}
# Output the resulting hashtable.
$hash
A note on the construction of the .NET [System.Text.RegularExpressions.Regex] object (or [regex] for short), [regex] '(?im)^([a-z]+|\d{10})$':
Embedding matching options IgnoreCase and Multiline as inline options i and m directly in the regex string ((?im) is convenient, in that it allows using simple cast syntax ([regex] ...) to construct the regular-expression .NET object.
However, this syntax may be obscure and, furthermore, not all matching options are available in inline form, so here's the more verbose, but easier-to-read equivalent:
$re = New-Object regex -ArgumentList '^([a-z]+|\d{10})$', 'IgnoreCase, Multiline'
Note that the two options must be specified comma-separated, as a single string, which PowerShell translates into the bit-OR-ed values of the corresponding enumeration values.
other solution, use convertfrom-string
$template=#'
{name*:Lori}
{street:234 east 2nd street}
{city:Raleigh nc 12345}
{phone:9199617621}
{mail:lori#hotmail.com}
{name*:Robert}
{street:2531 10th Avenue}
{city:Seattle WA 93413}
{phone:2068869421}
{mail:robert#hotmail.com}
{name*:Robert}
{street:2531 Avenue}
{city:Seattle WA 93413}
{phone:2068869421}
{mail:robert#hotmail.com}
'#
Get-Content -Path "c:\temp\file*.txt" | ConvertFrom-String -TemplateContent $template | select name, phone

PowerShell: get data between two strings

I've got next data into variable $out (type is Object[]):
Success...
Go!
0:#217> trace && .quit
0x000 Subline : _OK
0x008 Timed : NO
0x016 Check : _OK
0x022 Post :
0x030 Offset : None
0x038 Hint : False
0x050 NextHint : False
quit:
I need extract text between string 0:#217> trace && .quit and quit:
I wrote:
[Regex]::Match($out, "(?<=.quit').+?(?=quit:)").Value
But this extracts required data into a line (type String), not a column (Object[]). How to fix this?
P.S.
I solved the problem by myself as follows
([Regex]'(?is)(?:(?<=\.quit).+(?=quit:))').Match(($out -join "`n")).Value
But maybe there is more perfect way to do this?
Problem solved
([Regex]'\s+0x([^q]+)').Match(($out -join "`n")).Value
Just use the -split function to create a String[] of your result:
$result = ([regex]::Match($a, '\.quit(.*)quit:').Groups[1].value) -split [System.Environment]::NewLine
[Edit: this will work if $out is a String[], e.g. from $out = Get-Content results.txt, from your other comments, you might have something different].
As a general approach, have a true/false flag which chooses whether lines are allowed through or not, and when you see the first line then set the flag, and when you see the last line you want, change the flag.
$middle = foreach ($line in $out) {
if ($line -match '^quit') { $allow = $false }
if ($allow) { write-output $line }
if ($line -match '0:#217>') { $allow = $true }
}
The ordering of the tests determines whether the start or end lines show up in the results or not.
This can be shortened on the console for typing, into something like:
# loop # end line clears flag # print? # start line sets flag
$out |% { if($_ -match '^quit'){$f=0}; if ($f){$_}; if ($_ -match '0:#217>'){$f=1} }
This might work, but it makes some assumptions:
$out -match '^ '
$out is always a String[]. Force it with #($out) if it might be a single string.
You are using PowerShell v4 or v5, so -operator will act as a filter on an array.
Your example data is accurate, and all the lines you want start with a space, and all the other lines do not.
The following is for very simple searches, get the string data between a starting string and ending string.
Upsides
Very simplistic
Downsides
Does poorly for data with multiple matches etc
Code
Function Get-StringBetweenStartEnd {
Param($Text,$Start,$End)
$Regex = [Regex]::new("(?<="+$Start+")(.*)(?="+$End+")")
$Match = $Regex.Match($String)
if($Match.Success) { Return $Match.Value}else{Return ""}
}
Example Usage
$String = "Test: disconnected: 10.10.10.1::59270 (VNC Viewer closed)"
$Result = Get-StringBetweenStartEnd -Text $String -Start "nected:" -End "::"
$Result.Trim()
Output:
10.10.10.1

PowerShell - open a file, find a string in a line and replace a string in a previous line

there is a sample data file
Session 1: {21AD8B68-2A42-459e-BD29-F082F47E71B2}
Started: 06-24-2015 11:00
NDS Tree: TEST_TREE
AD Server: dc01.adatum.com
O=BRANCH/OU=BRANCH_CITY1/CN=user1
User
CN=user1,OU=BRANCH_CITY1,OU=ADATUM,DC=adatum,DC=com
user
O=BRANCH/OU=BRANCH_CITY1/CN=EVERYONE1
Group
CN=EVERYONE1,OU=BRANCH_CITY1,OU=ADATUM,DC=adatum,DC=com
group
O=BRANCH/OU=BRANCH_CITY2/CN=user2
User
CN=user2,OU=BRANCH_CITY2,OU=ADATUM,DC=adatum,DC=com
user
O=BRANCH/OU=BRANCH_CITY2/CN=EVERYONE2
Group
CN=EVERYONE2,OU=BRANCH_CITY2,OU=ADATUM,DC=adatum,DC=com
group
I would like to find a line that contains a string "group" (case sensitive) or "user" (case sensitive). If there will be a match, a line before should be changed like this:
if "user" change a line before to CN=<...>,OU=ADATUM,DC=adatum,DC=com
if "group" change a line before to CN=<...>,OU=GROUPS,OU=ADATUM,DC=adatum,DC=com
Of course, an output is a data file that contains all changes.
Any idea?
Many thanks in advance,
M.
The easiest way to accomplish this is probably by using a regular for loop to keep track of line numbers - if line $n matches "user", replace the string in line $n-1.
To do a case-sensitive regex, use -cmatch (notice the c prefix). In the example below I've used a named capture group ((?<name>pattern)) to match and capture either user or group.
The last part, adding a new path to the existing CN=<...> part can be accomplished with the -split command and a lookbehind to avoid messing up escaped commas in the CN value:
# Read file, line by line
$SampleFile = #(Get-Content C:\path\to\data.txt)
# Loop over the text by line numbers
for($i=0;$i -lt $SampleFile.Count;$i++){
# Test if the line matches
if(![string]::IsNullOrWhiteSpace($SampleFile[$i]) -and $SampleFile[$i].Trim() -cmatch "(?<type>^group|user$)"){
# If so, use the match to determine the DN suffix
switch($Matches["type"]){
"group" { $SampleFile[$i-1] = "{0},OU=GROUPS,OU=ADATUM,DC=adatum,DC=com" -f ($SampleFile[$i-1] -split "(?<!\\),")[0] }
"user" { $SampleFile[$i-1] = "{0},OU=ADATUM,DC=adatum,DC=com" -f ($SampleFile[$i-1] -split "(?<!\\),")[0] }
}
}
}
$SampleFile | Out-File C:\path\to\output.txt -Force
Something like this should do the trick:
$c = Get-Content .\file_name.txt
for ($i = 0; $i -lt $c.length; $i++) {
if ($c[$i] -cmatch "^group" ) {
$c[$i-1] = "CN=<...>,OU=ADATUM,DC=adatum,DC=com"
}
elseif ($c[$i] -cmatch "^user") {
$c[$i-1] = "CN=<...>,OU=GROUPS,OU=ADATUM,DC=adatum,DC=com"
}
}
$c | Out-File .\new_file.txt