I am trying to parse an INF; specifically, driver version from the file. I am new to PowerShell, so I've gotten only this far.
The file looks like this:
[Version]
Signature = "$WINDOWS NT$"
Class = Bluetooth
ClassGuid = {e0cbf06c-cd8b-4647-bb8a-263b43f0f974}
Provider = %PROVIDER_NAME%
CatalogFile = ibtusb.cat
DriverVer=11/04/2014,17.1.1440.02
CatalogFile=ibtusb.cat
The second last line has the information I am looking for. I am trying to parse out just 17.1.1440.02.
One file may contain multiple lines with DriverVer=..., but I am only interested in the first instance.
Right now I've the following script.
$path = "C:\FilePath\file.inf"
$driverVersoin = Select-String -Pattern "DriverVer" -path $path
$driverVersoin[0] # lists only first instance of 'DriverVer'
$driverVersoin # lists all of the instances with 'DriverVer'
Output is:
Filepath\file.inf:7:DriverVer=11/04/2014,17.1.1440.02
But I am only looking for 17.1.1440.02
Make your expression more specific and make the part you want to extract a capturing group.
$pattern = 'DriverVer\s*=\s*(?:\d+/\d+/\d+,)?(.*)'
Select-String -Pattern $pattern -Path $path |
select -Expand Matches -First 1 |
% { $_.Groups[1].Value }
Regular expression breakdown:
DriverVer\s*=\s* matches the string "DriverVer" followed by any amount of whitespace, an equals sign and again any amount of whitespace.
(?:\d+/\d+/\d+,)? matches an optional date followed by a comma in a non-capturing group ((?:...)).
(.*) matches the rest of the line, i.e. the version number you want to extract. The parentheses without the ?: make it a capturing group.
Another option (if the version number is always preceded by a date) would be to just split the line at the comma and select the last field (index -1):
Get-Content $path |
Where-Object { $_ -like 'DriverVer*' } |
Select-Object -First 1 |
ForEach-Object { $_.Split(',')[-1] }
Related
I'm trying to get a certain string of numbers (the numbers vary in length on each reload) out of a script tag on a website. However, I am struggling to figure out how to do it as I am stuck with PowerShell v2 and cannot upgrade it higher.
I've managed to get the full script by getting element by loading the site in IE and getting element by tag name "script" and I've attempted to try some regex to find the string but can't quite figure it out.
I have also tried stripping the characters off the front and back of the script, that's when I realised the lengths of the numbers change each time.
Part of the script is:
var value = document.wizform.selActivities.options[document.wizform.selActivities.selectedIndex].value;
if (value == "Terminate") {
if (confirm("Are you sure you want to terminate the selected business process(es)?")) {
document.wizform.action = "./Page?next=page.actionrpt&action=terminate&pos=0&1006999619";
javascript:document.wizform.submit();
}
} else if (value == "TerminateAndRestart") {
if (confirm("Are you sure you want to terminate and restart the selected business process(es)?")) {
document.wizform.action = "./Page?next=page.actionrpt&action=terminateandrestart&pos=0&237893352";
javascript:document.wizform.submit();
}
}
The part I want to capture is the numbers here
document.wizform.action = "./Page?next=page.actionrpt&action=terminateandrestart&pos=0&237893352";
The PowerShell code I have so far is
$checkbox = $ie.Document.getElementsByTagName("script") | Where-Object {
$_.outerHTML -like "*./Page?next=page.actionrpt&action=terminate*"
} # | select -Expand outerHTML
$content = $checkbox
$matches = [regex]::Matches($content, '".\action=terminate\.([^"]+)')
$matches | ForEach-Object {
$_.Groups[1].Value
}
What I would like is PowerShell to have just the number as a variable, so in the example above I would like to be able to have either 0&237893352 or just 237893352 (as the note does not change, so I can add the 0& back in after if I need to).
Use a positive lookbehind assertion for matching the particular action you're interested in:
$re = '(?<=action=terminateandrestart&pos=)0&\d+'
$content |
Select-String -Pattern $re |
Select-Object -Expand Matches |
Select-Object -Expand Value
(?<=...) is a regular expression construct called "positive lookbehind assertion" that allows for matching something that is preceded by a particular string (in your case "action=terminateandrestart&pos=") without making that string part of the returned match. This way you can look for the string "action=terminateandrestart&pos=" followed by "0&" and one or more digits (\d+) and return only "0&" and the digits.
I am working on some PowerShell scripting stuff not having a strong PowerShell skill under my belt. Basically I am more into Java, but I need to use PowerShell to get some things done at work.
What I have so far is a little snippet that parses my desired log file, returns an object and writes it to a file.
# find all lines with "successfully installed":
Select-String -Path $env:windir\WindowsUpdate.log -Pattern 'successfully installed' |
ForEach-Object {
$information = $_ | Select-Object -Property Date, Product
$parts = $_.Line -split '\t'
[DateTime]$information.Date = $parts[0] + ' ' + $parts[1].SubString(0,8)
$information.Product = ($_.Line -split 'following update: ')[-1]
$information
} | Out-File parsedUpdate.log
My output looks like this
What I would love to do next is to
Get rid of the line that labels the Properties and the line below it as well as I would send the output to EventLog soon.
Select only the lines that are no older than a specific date.
So how would I go about rejecting these two lines? Regarding the date problem I would love the exclude lines older than a specific date which I specify.
I have read that that Select-String has an -Exclude <String> property.
Would it be smart (and of course possible) to use this Directive to Exclude lines with a specific date and how would I do that - lets say for example, to reject any line older than a week from now?
thanks in advance.
Andrew
The timestamps on the actual log entries are in string sortable format, so you can do your date filtering early, before object creation:
$Start = (get-date).AddDays(-7).ToString('yyyy-MM-dd')
Select-String -Path $env:windir\WindowsUpdate.log -Pattern 'successfully installed' |
Where {$_.line -gt $Start} |
All you have to do for the (2) part of your question is add a filter to your result set, since you've already got objects with the Date property. The filter clause will look like Where-Object { $_.Date -ge "2/6/2015" } just replace with the date you are interested in.
# find all lines with "successfully installed" no older than 2/6/2015:
Select-String -Path $env:windir\WindowsUpdate.log -Pattern 'successfully installed' |
ForEach-Object {
$information = $_ | Select-Object -Property Date, Product
$parts = $_.Line -split '\t'
[DateTime]$information.Date = $parts[0] + ' ' + $parts[1].SubString(0,8)
$information.Product = ($_.Line -split 'following update: ')[-1]
$information
} | Where-Object { $_.Date -ge "2/6/2015" } | Out-File parsedUpdate.log
I need to parse a text file and retrieve data from it... based on other data in the same file..
I need to find the lines that say not ok.. and then find the Nodes they are under..
I know how to pull the data in..and find the Not Ok's and the Nodes. I also have an idea that I'm sure is overly complicated to find what I'm looking for. I can parse the Node lines into an array so like
$test = (select-string -path C:\error.txt -Pattern "Node:").linenumber
then find the line number of the not oks and backup lol but this seems like the most difficult way to do this. I'm familiar with PS but not an expert.
$test2 = (select-string -path C:\error.txt -Pattern "Not ok").linenumber
so to spell out what I need ..
parse file for Node.. find lines below that are not ok.. and if so set node to variable...if not ok isn't found move on to next node.
Thanks for any help
example txt file below
Node: Server
*********************
Line 1 ok
line 2 ok
line 3 ok
Line 4 Not ok
line 5 ok
line 6 ok
*********************
Node: Server2
*********************
Line 1 ok
line 2 ok
line 3 Not ok
Line 4 ok
line 5 ok
line 6 ok
*********************
$errorNodes = #()
Get-Content C:\temp\test.txt | ForEach-Object {
if ($_ -imatch 'Node: (.+)$') {
$node = $Matches[1]
}
if ($_ -imatch 'not ok') {
$errorNodes += $node
}
}
$errorNodes
Explanation
Get-Content reads a file line by line.
For each line, first check to see if it's a node; if so, set the $node variable to the current node's name.
Then check to see if the line matches the text 'not ok'. If so, add the node name to the list of error nodes (the array variable $errorNodes.
So at the end, $errorNodes will contain the nodes with problems.
If your list is long, this should be a quicker way to parse (also less code :)):
$nodes = [Regex]::Split((Get-Content info.txt), 'Node:')
# '?' is an alias for Where-Object
$bad = $nodes | ? { $_.ToLower().Contains('not ok') }
$bad now also contains all the text under the node containing "not ok" (in the even there are multiple lines that are not ok).
This answer is most likely more complicated than it needs to be but it returns useful objects that, depending what else op needs to do in his code, can be useful for further processing. For this example I used the file structure of the OP and added some extra nodes to make the output a little more verbose.
$file = Get-Content "c:\temp\test.txt" -Raw
$file -split '\s+(?=Node: \w+)' |
%{ $stringData = (($_ -replace ": "," = ") -replace 'line\W+(\d+)\W+','Line$1 = ') -replace '\*+'
New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
} | select node,line* | Format-Table
Using PowerShell 3.0: The code will read the file as a whole string (Not creating a string array) using the -Raw parameter. The $file is the string split at the text "Node: " which will break up the Nodes as separate objects.
In order to create the custom object we need to make sure all the items of the Node contain name=value pairs. To accomplish this I nested some -replace operations.
$_ -replace ": "," = " - To change the first line to "Node = Servername"
-replace 'line\W+(\d+)\W+','Line$1 = ' - Convert Line # ok into Line# = Ok\Not Ok Where # is the particular line 1-6
-replace '\*+' - To remove the lines that contain just astericks ( or whatever the plural of astericks is)
The formated string is used as input for New-Object PSObject -Property $(ConvertFrom-StringData $Stringdata)
After that we can control the piped output like we would almost any other object. To ensure that node appears first in the list the select-object statement.
The following is my sample output:
Node Line4 Line5 Line6 Line1 Line2 Line3
---- ----- ----- ----- ----- ----- -----
Server Not ok ok ok ok ok ok
Server2 ok ok ok ok ok Not ok
Server4 ok ok ok Not ok ok ok
Server3 ok ok ok ok ok ok
I find something interesting when I try to read text files from a folder:
$files = Get-ChildrenItem "C:\MyFolder" -Recursive | ?{$_.Extension -like '.txt'}
foreach ($file in $files) {
[string]fileFullName = $file.FullName
$content = Get-Content -Path $fileName
....
}
I encountered some exceptions if file names contains [ and ] chars, for example, a[1].txt.
I guess that every thing is object in PS. Therefore, as for my understanding, [..] in file names maybe treated as index access. Is there any way to deal with files with brackets?
You want -LiteralPath:
$files = Get-ChildItem "C:\MyFolder" -Recurse | ? { $_.Extension -like '.txt' }
foreach ($file in $files) {
[string]$fileFullName = $file.FullName
$content = Get-Content -LiteralPath $fileFullName
$content
}
From detailed help () you willsee:
-LiteralPath
Specifies the path to an item. Unlike Path, the value of LiteralPath is used exactly as it is typed. No characters are
interpreted as wildcards. If the path includes escape characters,
enclose it in single quotation marks. Single quotation marks tell
Windows PowerShell not to interpret any characters as escape
sequences.
All, I'm very new to powershell and am hoping someone can get me going on what I think would be a simple script.
I need to parse a text file, capture certain lines from it, and save those lines as a csv file.
For example, each alert is in its own text file. Each file is similar to this:
--start of file ---
Name John Smith
Dept Accounting
Codes bas-2349,cav-3928,deg-3942
iye-2830,tel-3890
Urls hxxp://blah.com
hxxp://foo.com, hxxp://foo2.com
Some text I dont care about
More text i dont care about
Comments
---------
"here is a multi line
comment I need
to capture"
Some text I dont care about
More text i dont care about
Date 3/12/2013
---END of file---
For each text file if I wanted to write only Name, Codes, and Urls to a CSV file. Could someone help me get going on this?
I'm more a PERL guy so I know I could write a regex for capturing a single line beginning with Name. However I am completely lost on how I could read the "Codes" line when it might be one line or it might be X lines long until I run into the Urls field.
Any help would be greatly appreciated!
Text parsing usually means regex. With regex, sometimes you need anchors to know when to stop a match and that can make you care about text you otherwise wouldn't. If you can specify that first line of "Some text I don't care about" you can use that to "anchor" your match of the URLs so you know when to stop matching.
$regex = #'
(?ms)Name (.+)?
Dept .+?
Codes (.+)?
Urls (.+)?
Some text I dont care about.+
Comments
---------
(.+)?
Some text I dont care about
'#
$file = 'c:\somedir\somefile.txt'
[IO.File]::ReadAllText($file) -match $regex
if ([IO.File]::ReadAllText($file) -match $regex)
{
$Name = $matches[1]
$Codes = $matches[2] -replace '\s+',','
$Urls = $matches[3] -replace '\s+',','
$comment = $matches[4] -replace '\s+',' '
}
$Name
$Codes
$Urls
$comment
If the file is not too big to be processed in memory, the simple way is to read it as an array of strings. (What too big means is subject to your system. Anything sub-gigabyte should work without too much a hickup.)
After you've read the file, set up a head and tail counters to point to element zero. Move the tail pointer row by row forward, until you find the date row. You can match data with regexps. Now you know the start and end of a single record. For the next record, set head counter to tail+1, tail to tail+2 and start scanning rows again. Lather, rinse, repeat until end of array is reached.
When a record is matched, you can extract name with a regex. Codes and Urls are a bit trickier. Match the Codes row with a regex. Extract it and all the next rows unless they do not match the code pattern. Same goes to Urls data. If the file always has whitespace padding on rows that are data to previous Urls and Codes, you could use match whitespace count with a regexp to get data rows too.
Maybe something line this would to it:
foreach ($Line in gc file.txt) {
switch -regex ($Line) {
'^(Name|Dept|Codes|Urls)' {
$Capture = $true
break
}
'^[A-Za-z0-9_-]+' {
$Capture = $false
break
}
}
if ($Capture) {
$Line
}
}
If you want the end result as a CSV file then you may use the Export-Csv cmdlet.
According the fact that c:\temp\file.txt contains :
Name John Smith
Dept Accounting
Codes bas-2349,cav-3928,deg-3942
iye-2830,tel-3890
Urls hxxp://blah.com
hxxp://foo.com
hxxp://foo2.com
Some text I dont care about
More text i dont care about
.
.
Date 3/12/2013
You can use regular expressions like this :
$a = Get-Content C:\temp\file.txt
$b = [regex]::match($a, "^.*Codes (.*)Urls (.*)Some.*$", "Multiline")
$codes = $b.groups[1].value -replace '[ ]{2,}',','
$urls = $b.groups[2].value -replace '[ ]{2,}',','
If all files have the same structure you could do something like this:
$srcdir = "C:\Test"
$outfile = "$srcdir\out.csv"
$re = '^Name (.*(?:\r\n .*)*)\r\n' +
'Dept .*(?:\r\n .*)*\r\n' +
'Codes (.*(?:\r\n .*)*)\r\n' +
'Urls (.*(?:\r\n .*)*)' +
'[\s\S]*$'
Get-ChildItem $srcdir -Filter *.txt | % {
[io.file]::ReadAllText($_.FullName)
} | Select-String $re | % {
$f = $_.Matches | % { $_.Groups } | ? { $_.Index -gt 0 }
New-Object -TypeName PSObject -Prop #{
'Name' = $f[0].Value;
'Codes' = $f[1].Value;
'Urls' = $f[2].Value;
}
} | Export-Csv $outfile -NoTypeInformation