Searching for a matching map name in Powershell - regex

sorry i really dont know how to properly ask this question.
I would like to parse CS:GO Demo files in Powershell, and i would like to retrive the map name from it.
I opening dem files like this:
Get-Content $demo | Select -First 1 | Select-String -Pattern 'de_'
And i get this as response:
HL2DEMO đ5 MatchServer I.
GOTV Demo
de_mirage
csgo
##A g uÔ ~ř˙˙
ą Vđk (8wEÄü€ŢMĐhZăU X#`śh u <zcsgo‚ de_mirageŠ ’sky_dustšGOTV¨ ° ¸  ( 0 ž
I would like to get only the de_mirage as a variable. So if a map changes, then it will be de_dust2 or de_inferno and so on. Does anybody know a solution for this?
Thank you!

When using Get-Content, each line is passed down the pipeline one at a time, unless specifying the -Raw switch. The reason I bring this up is due to your Select cmdlet that you're piping to. When you specified the parameter of -First, with a value of 1, you're only grabbing the first line, and then trying to find the pattern in the first line.
Here's my poor attempt at RegEx:
Get-Content -Path $demo | Where-Object -FilterScript { $_ -match 'de_\w+' }
$Matches[0]
. . .where the $Matches Automatic Variable contains all the matched RegEx patterns (as the name indicates) stored in an array format; where we use the index number to reference the value. This would also work piping to Select-String when searching for a Pattern just like you had done.

Related

Capturing group not working at end of -Pattern for Select-String

I've recently started working with regex in Powershell and have come across an unexpected response from the Select-String cmdlet.
If you enter something like the following:
$thing = "135" | Select-String -Pattern "(.*?)5"
$thing.Matches
You receive the expected result from the Match-Info object:
Groups : {135, 13}
Success : True
Captures : {135}
Index : 0
Length : 3
Value : 135
But if you place the capturing group at the end of the -Pattern:
$thing = "135" | Select-String -Pattern "(.*?)"
$thing.Matches
The Match-Info doesn't seem to find anything, although one is created:
Groups : {, }
Success : True
Captures : {}
Index : 0
Length : 0
Value :
As I said, I'm quite new to Powershell, so I expect this behavior is operator error.
But what is the work around? This behavior hasn't caused me problems yet, but considering the files I'm working with (electronic manuals contained in XML files), I expect it will eventually.
...
With regards,
Schwert
...
Clarification:
I made my example very simple to illustrate the behavior, but my original issue was with this pattern:
$linkname = $line | Select-String -Pattern "`"na`"><!--(?<linkname>.*?)"
The file is one of our indices for the links between manuals, and the name of the link is contained within a comment block located on each line of the file.
The pattern is actually a typo, as the name and the comment don't go all the way to the end of the line. I found it when the program began giving errors when it couldn't find "linkname" in the Match-Info object.
Once I gave it the characters which occur after the link name (::), then it worked correctly. Putting it into the example:
$linkname = $line | Select-String -Pattern "`"na`"><!--(?<linkname>.*?)::"
I'm no regex expert but I believe your pattern "(.*?)" is the problem. If you remove the ?, for example, you get the groups as expected.
Also, PLEASE don't use regex to parse XML. :) There's much easier ways to do that such as:
[xml]$Manual = Get-Content -Path C:\manual.xml
or
$xdoc = New-Object System.Xml.XmlDocument
$file = Resolve-Path C:\manual.xml
$xdoc.Load($file)
Once you've got it in a structured format you can then use dot notation or XPath to navigate the nodes and attributes.

How can I split and select from an array of filenames in Powershell?

I have a script I wrote in my company for clearing Citrix UPM profiles. Not very complicated, but it generates logs for every user it is run on. Along the format of:
UPMreset-e0155555-20150112-0733
UPMreset-n9978524-20150114-1128
UPMreset-jsmith-20150113-0840
etc.
So I want to grab the folder with all the .txt files, select only the username and count to see if one appears more than a certain number of times. To check for problem children. Putting them into an array is easy enough, but when doing a -split I can't seem to find a regex combination to select only the username. I thought I could just do a ('-')[1], but that doesn't appear to work. Do you have any suggestions?
$arrFiles = Get-Childitem "c:\logs"
$arrFiles | %{ $arrfile = $_ -split ('-'); Write-Host $arrfile[0]}
edit: Included test code for posterity sake.
I'd try something like this:
$Path = 'N:\Folder\*.txt';
Get-ChildItem $Path |
ForEach-Object {
Write-Output $_.BaseName.Split('-')[1];
} |
Group-Object |
Where-Object { $_.Count -gt 1 } |
Sort-Object -Property Name |
Select-Object Name, Count;
To answer the question.
$_ is one of the objects returned by Get-ChildItem. Those objects are not strings. They're .Net objects of type System.IO.DirectoryInfo or System.IO.FileInfo. That means if we use $_, we're referencing the whole object. Worse, neither of those objects has a Split() method, so $_.Split('-') would refer to a function that didn't exist.
BaseName is a property of a FileInfo or DirectoryInfo object. That property contains the name of the file without the path or the extension. Critically, this property is also a String, which does have the Split() method. So using this property does two things: It removes the path name and the extension since we don't care about that and we don't want it to potentially break something (e.g., if someone put a dash in the parent folder's name), and it gives us a String object which we can manipulate with String methods and do things like call the Split function.
Try something like this at the command line:
$x = Get-ChildItem 'N:\Folder\UPMreset-e0155555-20150112-0733.txt';
$x | Get-Member;
You'll get a huge list of Methods (functions) that the object can do and Properties (attribute values) of the object. Name, FullName, BaseName, and Extension are all very common properties to use. You should also see NoteProperties and CodeProperties, which are added by the PowerShell provider to make using them easier (they wouldn't be available in a C# program). The definition tells you how to call the method or what the type of the property is and what you can do with it. You can usually Google and find MSDN documentation for how to use them, although it's not always the easiest way to do things.
Compare the above to this:
$x.BaseName | Get-Member;
You can see that it's a String, that there all kinds of methods like Split, Replace, IndexOf, etc.
Another helpful one is:
$x | Select-Object *;
This returns all the Propety, NoteProperty, and CodeProperty values this object has.
This highlights one of the best ways to learn about what you can do with an object. Pipe it to Get-Member, and you learn the type and any methods or properties that you can access. That, combined with piping something to Select-Object *, can tell you a lot about what you're working with.
What problem were you having with .split('-')[1]?
$filenames = #(
'UPMreset-e0155555-20150112-0733',
'UPMreset-n9978524-20150114-1128',
'UPMreset-jsmith-20150113-0840'
)
$filenames |% {$_.split('-')[1]}
e0155555
n9978524
jsmith
It looks like the filenames are always UPMreset-, followed by the username. So use this:
UPMreset-(.+?)-
and the capture group will contain the username. It's using a lazy quantifier to get anything up to the next dash.
You could also do the split in a calculated property with Group-Object:
$FileNames = Get-ChildItem -Path $LogDir -Filter "*.txt" -Name
$FileNames | Group-Object #{Expression={($_ -split "-")[1]}} | Where-Object {$_.Count -gt 1}

How to get a value from Select-String

I have several files in a folder, those are .xml files.
I want to get a value from those files.
A line in the file, could look like this:
<drives name="Virtual HD ATA Device" deviceid="\\.\PHYSICALDRIVE0" interface="IDE" totaldisksize="49,99">
What i'm trying to do is get the value 49,99 in this case.
I am able to get the line out of the file with:
$Strings = Select-String -Path "XML\*.xml" -Pattern totaldisksize
foreach ($String in $Strings) {
Write-Host "Line is" $String
}
But getting just the value in "" i don't get how. I've also played around with
$Strings.totaldisksize
But no dice.
Thanks in advance.
You can do this in one line as follows:
$(select-string totaldisksize .\XML\*.xml).line -replace '.*totaldisksize="(\d+,\d+)".*','$1'
The Select-String will give you a collection of objects that contains information about the match. The line property is the one you're interested in, so you can pull that directly.
Using the -replace operator, every time the .line property is a match of totaldisksize, you can run the regex on it. The $1 replacement will grab the group in the regex, the group being the part in parentheses (\d+,\d+) which will match one or more digits, followed by a comma, followed by one or more digits.
This will print to screen because by default powershell will print an object to the screen. Because you're only accessing the .line property, that's the only bit that's printed and also only after the replacement has been run.
If you wanted to explicitly use a Write-Host to see the results, or do anything else with them, you could store to a variable as follows:
$sizes = $(select-string totaldisksize .\XML\*.xml).line -replace '.*totaldisksize="(\d+,\d+)".*','$1'
$sizes | % { Write-Host $_ }
The above stores the results to an array, $sizes, and you iterate over it by piping it to the Foreach-Object or %. You can then access the array elements with $_ inside the block.
But.. but.. PowerShell knows XML.
$XMLfile = '<drives name="Virtual HD ATA Device" deviceid="\\.\PHYSICALDRIVE0" interface="IDE" totaldisksize="49,99"></drives>'
$XMLobject = [xml]$XMLfile
$XMLobject.drives.totaldisksize
Output
49,99
Or walk the tree and return the content of "drives":
$XMLfile = #"
<some>
<nested>
<tags>
<drives someOther="stuff" totaldisksize="49,99" freespace="22,33">
</drives>
</tags>
</nested>
</some>
"#
$drives = [xml]$XMLfile | Select-Xml -XPath "//drives" | select -ExpandProperty node
Output
PS> $drives
someOther totaldisksize freespace
--------- ------------- ---------
stuff 49,99 22,33
PS> $drives.freespace
22,33
XPath query of "//drives" = Find all nodes named "drives" anywhere in the XML tree.
Reference: Windows PowerShell Cookbook 3rd Edition (Lee Holmes). Page 930.
I am not sure about powershell but if you prefer using python below is the way of doing it.
import re
data = open('file').read()
item = re.findall('.*totaldisksize="([\d,]+)">', data)
print(item[0])
Output
49,99

Regular Expression Remove large portions of string

I am looking to extract the name value from the output returned by the line...
Gwmi win32_groupuser –computer $env:computername | ? {$_.groupcomponent –like '*"Administrators"'} | Select -Property PartComponent
I would think that I would use a regular expression to trim but I am unfamiliar with their operation and (as of yet) have been unable to find instructions sufficient to complete this.
For reference, the output is something like...
\\My_Machine\root\cimv2:Win32_UserAccount.Domain="My_Machine",Name="Administrator"
And I would like to extract 'Administrator' from that output.
Right now I'm trying...
$Report = Gwmi win32_groupuser –computer $env:computername | ? {$_.groupcomponent –like '*"Administrators"'} | Select -Property PartComponent
$Report | ForEach-Object {$_.PartComponent -match '(?<=Name=")[^"]+[^"]'
[PSCustomObject]#{Resultant_String=$Matches.Values}}
and I'm getting...
Resultant_String
{Administrator}
True
{admin}
True
{GroupName}
True
{UserName}
True
{CiscoHistRprtUsr}
Try something like this, (?<=Name=")[^"]+[^"]. What this is doing is a positive look behind for Name=" and then any char (except ") in repetition and then any char that isn't ". This is a little more robust, since this will still work if the Name= is not the last element in your string. If Name= is say the first value returned, it should still only capture what is in the quotes directly following it, and not everything else until the last ".
Using your test example, I did some testing here.
If Name is always at the end of the string, you can simply use
([^"]*)"$
Explanation here : http://regex101.com/r/yV3uD6
Two solutions:
(?<=Name=")[^"]*?(?=")
Note that the the ? after the * is important here. It makes the quantifier non-greedy (it's just a fancy way to say that it will capture as few characters as possible and never go beyond the closing ").
(?:Name=")([^"]*?)(?:")
Since the first and the last groups are non-capturing, you just have to retrieve the value of the second group (which is actually the first and only one in terms of capture) with something like \1 or $1.
Ultimately I ended up with this.
$FileServer = "My_File_Server"
$LocalHostName = $env:computername
$OutPutPath = "\\$FileServer\system_information$\"
$GetAdmins = Gwmi win32_groupuser –computer $LocalHostName | ? {$_.groupcomponent –like '*"Administrators"'} | Select -Property PartComponent
ForEach-Object{$GetAdmins | % {if ($_ -match 'Name="(.+)"') {[PSCustomObject]#{Name=$Matches[1]}}}} | Export-Csv -Path "$OutPutPath\$LocalHostName\$ReportName.csv" -NoTypeInformation}
It produces a table with a name header that has the username of all the local admins extracted from the returned string.

Powershell using a regex to create a new hash table object

I'm trying to parse a file that contains blocks of text in this format(there can be 1-n of these):
-----------------------------------------------------------
KB Article Number(s): 2028769, 2072493, 2120979, 2143880, 2163958, 2163980, 980653, 980883, 981155, 981867, 982321, 982850
Language: All (Global)
Platform: x64
Location: (http://foo.bar.com)
Password: foo
-----------------------------------------------------------
The text is coming from an MS Hotfix request email if anyone is interested.
I have the following powershell one liner:
$file | select-string "(?<Name>\w+):(?<Value>.*)" -allmatches | SELECT #{N="Name";E={$_.Matches[0].Groups[1].Value}}, #{N="Value";E={$_.Matches[0].Groups[2].Value}}
This give me a flat set of name value pairs, I would like it to return an array of hashtables with each of the 5 Keys populated.
I'm not sure if the problem is my regex or how I'm accessing the matches (There must be a less verbose way of picking these out).
It's not pretty but I think it will work for you:
$ht = #{}
$file | %{if ($_ -match '(?<Name>[^:]+):(?<Value>.*)') {
$ht.($matches.Name) = $matches.Value.Trim()} `
elseif ($ht.Count) {$ht;$ht.Clear()}}
Using the -match operator is a tad easier because you can use the $matches variable directly without having to go through Select-String's MatchInfo.Matches property. BTW I'm assuming here $file was populated by a call to Get-Content i.e. each line is a string in a string array.