I want to display "SWEUserName=r.m12345"
Process RequestDetail 4 f45d912121f4:0 2017-01-01 10:04:13 SWE Command Processor - Handle user request: SWEFo=SWEEntryForm SWESD=3 SWENeedContext=false SWENoHttpRedir=false SWECmd=ExecuteLogin W=t SWEUserName=r.m12345 SWESPNR= SQOIWO= SWSAH= SWEH= _tid=12345678 SWEC=0 SWEW= SWEBID=-4 SRN= SWETS=1234567890 SWEWN=
Code: $Display | Select-String -Pattern 'SWEUserName*' -CaseSensitive -SimpleMatch
Expected Output: SWEUserName=r.m12345
You should be able to use a regular expression for this.
Example below:
$sample = 'Process RequestDetail 4 f45d912121f4:0 2017-01-01 10:04:13 SWE Command Processor - Handle user request: SWEFo=SWEEntryForm SWESD=3 SWENeedContext=false SWENoHttpRedir=false SWECmd=ExecuteLogin W=t SWEUserName=r.m12345 SWESPNR= SQOIWO= SWSAH= SWEH= _tid=12345678 SWEC=0 SWEW= SWEBID=-4 SRN= SWETS=1234567890 SWEWN='
$isMatch = $sample -match 'SWEUserName=[^\s]+'
if ($isMatch) {
Write-Output $Matches[0]
}
From: https://www.regular-expressions.info/powershell.html
As a side effect, the -match operator sets a special variable called $matches. This is an associative array that holds the overall regex match and all capturing group matches. $matches[0] gives you the overall regex match, $matches[1] the first capturing group, and $matches['name'] the text matched by the named group "name".
Related
I need to filter out only the AD groups from the Net User command in powershell. All the AD groups begin with an * so i would like to filter out the string by displaying everything that's preceeded by an *
I get an error since '*' is a special character and cannot be used. How do i get powershell to ignore it as a special character?
I cannot use any other commands to get AD groups, so Get-AD is not an option. i only have Net user to work with.
My base script,
Net User USER /domain | Select-String '*'
I cannot use any other script than Net user to accomplish this task, even though Get-AD would be simpler, i do not have the option.
Santiago's helpful answer shows a more robust, OO solution that is much more in the spirit of PowerShell.
To answer your question as asked:
Select-String by default interprets its (positionally implied) -Pattern argument as a regex (regular expression), where * is a metacharacter.
While \-escaping regex metacharacters is possible (and is necessary in the event that you need more sophisticated matching that requires a regex), the direct solution is to add the -SimpleMatch switch, which causes the -Pattern argument to be interpreted as a literal (verbatim) string:
net user $someuser /domain | Select-String * -SimpleMatch
Also note that what Select-String outputs by default aren't just the matching input lines as-is, but Microsoft.PowerShell.Commands.MatchInfo objects that provide metadata for each match, with the matching line text stored in the .Line property.
While that distinction doesn't matter much for displaying results, it may for programmatic processing, so if you only want to output the text of the matching lines, add -Raw in PowerShell (Core) 7+, or pipe to | ForEach-Object Line in Windows PowerShell.
The above will show those net user output lines that contain a literal *, and therefore all group memberships, which is good enough for the human observer.
You indeed need regex matching and operations if you want to extract the group names individually, for later programmatic processing:
# Use an ordered hashtable to collect the group names in,
# with keys 'Local' and 'Global', targeting the *current* user in this example.
$groupMemberships = [ordered] #{}
(((net user $env:USERNAME) -join "`n") -split "`n`n")[-1] -split '\n' -match ' \*' |
ForEach-Object {
$tokens = $_ -split ' \*'
if ($tokens[0] -notmatch '^ ') {
$key = if ($groupMemberships.Count -eq 0) { 'Local' } else { 'Global' }
}
$groupMemberships[$key] += #($tokens[1..($tokens.Count-1)].Trim())
}
$groupMemberships # display result.
Sample output:
Name Value
---- -----
Local { Administrators }
Global { Department1, Region1 }
That is $groupMemberships.Local $groupMemberships.Global then contains the name(s) of the local / global (AD) groups the user is a member of, respectively, as an array.
Note:
The solution above is complex, because it tries to be as robust as possible.
Notably, it is possible - albeit not likely in practice - that output lines that are unrelated to group names contain * as well, notably the Comment and User's comment fields.
Therefore, only the last paragraph of net user's output is considered, which is known to contain the group names - note that matching lines by field-name parts such as Local and Global is explicitly avoided, as the field names are localized based on your system's display language.
The last paragraph is known to list the local group memberships first, followed by the global (AD) ones. Each line in the last paragraph can contain multiple (*-prefixed) group names and there can be overflow lines for additional groups that don't fit on the first line for the given scope. Such overflow flow lines can be detected by starting with whitespace.
Instead of trying to parse the output from net user USER /domain I would use what's already available in powershell. You can get the current logged on user's Active Directory Group Membership using adsi and adsisearcher.
Here are 2 different ways of accomplishing it.
By querying the user's memberof attribute:
$searcher = [adsisearcher]::new(
[adsi] "LDAP://$env:USERDNSDOMAIN",
[string] "(cn=$env:USERNAME)",
[string[]] ("memberOf", "cn")
)
$searcher.FindOne().Properties['memberof'] | ForEach-Object {
$searcher.Filter = "(distinguishedName=$_)"
$searcher.FindOne().Properties['cn'][0]
}
By querying all groups having the user as a member:
$searcher = [adsisearcher]::new(
[adsi] "LDAP://$env:USERDNSDOMAIN",
[string] "(cn=$env:USERNAME)",
[string[]] ("distinguishedName", "cn")
)
$userDn = $searcher.FindOne().Properties['distinguishedName'][0]
$searcher.Filter = "(&(objectCategory=group)(member=$userDn))"
$searcher.FindAll() | ForEach-Object {
$_.Properties['cn'][0]
}
You can use a backslash to escape regex special characters and use ^ to specify start of string:
> #("a", "*b", "c*", "*d", "e**") | Select-String -Pattern '^\*'
*b
*d
So, to display the groups you could use, for example:
Net User USER /domain | % { $_ -split "\s+" -match "^\*" }
As per the comment, if the group names may contain spaces then obviously splitting on space characters would be inappropiate.
An alternative:
Net User USER /domain | % { $_ -split '^[^*]+\*?' -match '.+' }
Or, if we only want to look at the lines beginning "Local Group Memberships" or "Global Group Memberships" we could use, for example:
Net User USER /domain |
? { $_ -match '^(?:Local|Global) Group Memberships +\*(.+)' } | % { $matches[1] }
Beginner here, I am working on a error log file and library, the current step I am on is to pull specific information from a txt file.
The code I have currently is...
$StatusErr = "Type 1","Type 2"
for ($i=0; $i -lt $StatusErr.length; $i++) {
get-content C:\blah\Logs\StatusErrors.TXT |
select-string $StatusErr[$i] |
add-content C:\blah\Logs\StatusErrorsresult.txt
}
while it is working, I need it to display as
Type-1-Description
2-Description
Type-1-Description
2-Description
Type-1-Description
2-Description
etc.
it is currently displaying as
Type 1 = Type-1-Description
Type 1 = Type-1-Description
Type 1 = Type-1-Description
Type 2 = 2-Description
Type 2 = 2-Description
Type 2 = 2-Description
I am unsure how to change the arrangement and remove unneeded spaces and the = sign
You need to search for both patterns in a single Select-String call in order to get matching lines in order.
While the -Pattern parameter does accept an array of patterns, in this case a single regex will do.
You need to use a regex pattern in order to capture and output only part of the lines that match.
$StatusErrRegex = '(?<=Type [12]\s*=\s*)[^ ]+'
get-content C:\blah\Logs\StatusErrors.TXT |
select-string $StatusErrRegex |
foreach-object { $_.Matches.Value } |
set-content C:\blah\Logs\StatusErrorsresult.txt
Note that I've replaced add-content with set-content, as I'm assuming you don't want to append to a preexisting file. set-content writes all objects it receives via the pipeline to the output file.
Select-String outputs Microsoft.PowerShell.Commands.MatchInfo instances whose .Matches property provides access to the part of the line that was matched.
For an explanation of the regex and the ability to experiment with it, see this regex101.com page.
Additional notes:
Select-String, like PowerShell in general, is case-insensitive by default; add the -CaseSensitive switch, if needed.
(?<=...) is a (positive) lookbehind assertion, whose matching text doesn't became part of what the regex captures.
\s* matches zero or more whitespace characters; \s+ would match one or more.
[^ ]+ matches one or more (+) characters that are not ^ spaces ( ), and thereby captures the run of non-space characters to the right of the = sign.
To match any of multiple words at the start of the pattern, use a regex alternation (|), e.g. '(?<=(type|data) [12]\s*=\s*)[^ ]+'
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.
I need to extract a list with strings that are between two special characters (= and ;).
Below is an example of the file with line types and the needed strings in bold.
File is a quite big one, type is xml.
<type="string">data source=**HOL4624**;integrated sec>
<type="string">data source=**HOL4625**;integrated sec>
I managed to find the lines matching “data source=”, but how to get the name after?
Used code is below.
Get-content regsrvr.txt | select-string -pattern "data source="
Thank you very much!
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4624;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4625;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
The XML is not valid, so it's not a clean parse, anyway you can use string split with regex match:
$html = #"
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4624;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
<RegisteredServers:ConnectionStringWithEncryptedPassword type="string">data source=HOL4625;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096</RegisteredServers:ConnectionStringWithEncryptedPassword>
"#
$html -split '\n' | % {$null = $_ -match 'data source=.*?;';$Matches[0]} |
% {($_ -split '=')[1] -replace ';'}
HOL4624
HOL4625
Since the connectionstring is for SQL Server, let's use .Net's SqlConnectionStringBuilder to do all the work for us. Like so,
# Test data, XML extraction is left as an exercise
$str = 'data source=HOL4624;integrated security=True;pooling=False;multipleactiveresultsets=False;connect timeout=30;encrypt=False;trustservercertificate=False;packet size=4096'
$builder = new-object System.Data.SqlClient.SqlConnectionStringBuilder($str)
# Check some parameters
$builder.DataSource
HOL4624
$builder.IntegratedSecurity
True
You can expand your try at using Select-String with a better use of regex. Also, you don't need to use Get-Content first. Instead you can use the -Path parameter of Select-String.
The following Code will read the given file and return the value between the = and ;:
(Select-String -Path "regsrvr.txt" -pattern "(?:data source=)(.*?)(?:;)").Matches | % {$_.groups[1].Value}
Pattern Explanation (RegEx):
You can use -pattern to capture an String given a matching RegEx. The Regex can be describe as such:
(?: opens an non-capturing Group
data source= matches the charactes data source=
) closes the non-capturing Group
(.*?) matches any amount of characters and saves them in a Group. The ? is the lazy operator. This will stop the matching part at the first occurence of the following group (in this case the ;).
(?:;) is the final non-capturing Group for the closing ;
Structuring the Output
Select-String returns a Microsoft.PowerShell.Commands.MatchInfo-Object.
You can find the matched Strings (the whole String and all captured groups) in there. We can also loop through this Output and return the Value of the captured Groups: | % {$_.groups[1].Value}
% is just an Alias for For-Each.
For more Informations look at the Select-String-Documentation and try your luck with some RegEx.
I basically want to use powershell and get the console user like
"query session | findstr console | awk '{print $2}'"
but not using awk, but I can't get it to work.
$out = query session | findstr console # good
$consoleuser = $out.split('\s+')[1] # doesn't work
$out looks something like:
>console joe 2 Active
$consoleuser ends up being:
ole joe 2 Active
As others have suggested try the following
$out = query session | findstr console
$consoleuser = $($out -split('\s+'))[1]
Or you could try
$consoleuser = $ENV:username
.Split() is a .Net string method, it doesn't use regexes. -split is the PowerShell operator and uses regexes.
And calling out to findstr is usable, but there's no need to leave PS to use it. e.g.
$out = query session | Where {$_ -match 'console'}
$consoleuser = ($out -split '\s+')[1]
((query session) -match 'console' -split '\s+')[1]
To complement TessellatingHeckler's helpful answer with a further optimization (but note that armorall171's helpful recommendation to simply use $env:USERNAME may be all you need):
(-split ((query session) -match '^>console'))[1]
Output from external command query session is returned as an array of strings by PowerShell, and the -match operator filters that array down to only the matching elements (just 1 line in this case).
The -split operator has a unary form whose behavior is similar to awk's default field-parsing behavior:
It splits the input into array elements by runs of whitespace, ignoring leading and trailing whitespace.
Example:
> -split " ab `t `t cde `n `n efgh "
ab
cde
efgh
Try
($out -Split '\s+')[1]
More useful stuff here