I have slight problem.
We have a PowerShell script that sets an expiration date in the 'Notes:' field in the AD.
What i want to do is to be able to remove/update this w/o removing other data in the field.
Example of 'Notes:' field (for ie. user X):
GR1234567890 expires on 20251125
END
If i use following code to try and isolate everything but the line starting with GR in it.
$UserName = Get-ADUser -Filter {SAMAccountName -eq "X"} -Properties Info
$UserName.Info | Select-String -Pattern 'GR[\s\S].+' -NotMatch
I get a "full match" and no output at all.
And if i remove -NotMatch i get a full match and full output of 'Notes:' field.
I've tried the RegEx in some of the RegEx online testers out there and there it works as expected. It is like there are no LF/CR or some wierd encoding on the output when traversing the pipeline...
I could do a match GR, a date and everything in between i guess... but id like for knowledge sake want to know if the above thinking is not possible or totally wrong (RegEX is not my strongest suit).
Problem was indeed the code itself as pointed out by Wiktor. Hats of to him.
$UserName.Info -Replace '^GR.+'
Will remove the line i want removed.
Related
I am trying to extract the Get-Help comment headers from a PowerShell script...using PowerShell. The file I'm reading looks something like this:
<#
.SYNOPSIS
Synopsis goes here.
It could span multiple lines.
Like this.
.DESCRIPTION
A description.
It could also span multiple lines.
.PARAMETER MyParam
Purpose of MyParam
.PARAMETER MySecondParam
Purpose of MySecondParam.
Notice that this section also starts with '.PARAMETER'.
This one should not be captured.
...and many many more lines like this...
#>
# Rest of the script...
I would like to get all the text below .DESCRIPTION, up to the first instance of .PARAMETER. So the desired output would be:
A description.
It could also span multiple lines.
Here's what I've tried:
$script = Get-Content -Path "C:\path\to\the\script.ps1" -Raw
$pattern = '\.DESCRIPTION(.*?)\.PARAMETER'
$description = $script | Select-String -Pattern $pattern
Write-Host $description
When I run that, $description is empty. If I change $pattern to .*, I get the entire contents of the file, as expected; So there must be something wrong with my RegEx pattern, but I can't seem to figure it out.
Any ideas?
(get-help get-date).description
The `Get-Date` cmdlet gets a DateTime object that represents the current date
or a date that you specify. It can format the date and time in several Windows
and UNIX formats. You can use `Get-Date` to generate a date or time character
string, and then send the string to other cmdlets or programs.
(get-help .\script.ps1).description
the Select-String cmdlet works on entire strings and you have given it ONE string. [grin]
so, instead of fighting with that, i went with the -match operator. the following presumes you have loaded the entire file into $InStuff as one multiline string with -Raw.
the (?ms) stuff is two regex flags - multiline & singleline.
$InStuff -match '(?ms)(DESCRIPTION.*?)\.PARAMETER'
$Matches.1
output ...
DESCRIPTION
A description.
It could also span multiple lines.
note that there is a blank line at the end. you likely will want to trim that away.
In the words of #Mathias R. Jessen:
Don't use regex to parse PowerShell code in PowerShell
Use the PowerShell parser instead!
So, let's use PowerShell to parse PowerShell:
$ScriptFile = "C:\path\to\the\script.ps1"
$ScriptAST = [System.Management.Automation.Language.Parser]::ParseFile($ScriptFile, [ref]$null, [ref]$null)
$ScriptAST.GetHelpContent().Description
We use the [System.Management.Automation.Language.Parser]::ParseFile() to parse our file and ouput an Abstract Syntax Tree (AST).
Once we have the Abstract Syntax Tree, we can then use the GetHelpContent() method (exactly what Get-Help uses) to get our parsed help content.
Since we are only interested in the Description portion, we can simply access it directly with .GetHelpContent().Description
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.
I am trying to capture the now playing song on this radio station when it is displayed on the website, I'm at the beginning of writing the script, so far I have the following code:
$webpage = (Invoke-WebRequest http://www.2dayfm.com.au).Content
$regex = [regex]"(.*nowPlayingInfo.*span)"
$regex.Match($webpage).Value.Split(">")[4].Replace("</span","")
This captures the website listed in the code, however there's two things an issue.
The first thing, when this code is run, it comes back with Loading... Reason for this, if I look at the result of this:
(Invoke-WebRequest http://www.2dayfm.com.au).Content | clip
Paste this into notepad, if I search for "Playing:" it has this line:
<p><span class="listenHeading">Playing:</span> <span id="nowPlayingInfo">Loading...</span></p>
When I run the Invoke-WebRequest in my code, it captures the website at that point in time, and to see this in real life, navigate in a browser to http://www.2dayfm.com.au/ and look right at the top where the Now Playing song is, it says Loading... for a short time before the song actually loads.
The other thing I was hoping is to remove the second line of the code and clean the regex up on the first line, so I don't need to use as many Split & Replace methods.
The other way I was trying to get this to work was by copying the XPATH from Chrome inspect element, the use something like
(Invoke-WebRequest -Uri 'http://www.2dayfm.com.au').Content | Select-Xml -XPath '//*[#id="nowPlayingInfo"]'
But this doesn't seem to work either, like it doesn't accept the XPATH, like the XPATH Chrome that thinks it is, is different to what PowerShell expects the XPATH to be.
Using a scraper isn't going to work because you get just the initial html content that is downloaded. The page uses Javascript/Ajax to render the song/artist info by manipulating the DOM after the initial download. However, you can use the InternetExplorer.Application COM object to do this:
$ie = New-Object -comObject InternetExplorer.Application
$ie.navigate('http://www.2dayfm.com.au/')
while ($ie.ReadyState -ne 4) { Start-Sleep -Seconds 1 } # need timeout here
$null = $ie.Document.body.innerhtml -match '\s+id\s*=\s*"nowPlayingInfo"\s*>(.*)</span'
$ie.Quit()
$matches[1]
Outputs:
Little Mix, Black Magic
The $null = bit is to just get rid of the True output that the -match operator generates (assuming the regex matches).
How do I use Select-String cmdlet to search a text file for a string which starts with a specific string, then contains random text and has another specific string towards the end of the line? I'm only interested in matches across a single line in the text file, not across the entire file.
For example I am searching to match both 'Set-QADUser' and 'WhatIf' on the same line in the file. And my example file contains the following line:
Set-QADUser -Identity $($c.ObjectGUID) -ObjectAttributes #{extensionattribute7=$ekdvalue} -WhatIf | Out-Null
How do I use Select-String along with a Regular Expression to locate the pattern in question? I tried using the following and it does work but it also matches other instances of either 'Set-QADUser' or 'WhatIf' found elsewhere in the text file and I only want to match instances when both search strings are found on the same line.
Select-String -path "test.ps1" -Pattern "Set-QADUser.*WhatIf" | Select Matches,LineNumber
To make this more complicated I actually want to perform this search from within the script file that is being searched. Effectively this is used to warn the user that the script being run is currently set to 'WhatIf' mode for testing. But of course the regEx matches the text from the actual Select-String cmd within the script when it's run - so it finds multiple matches and I can't figure out a very good way to overcome that issue. So far this is what I've got:
#Warn user about 'WhatIf' if detected
$line=Select-String -path $myinvocation.mycommand.name -Pattern "Set-QADUser.*WhatIf" | Select Matches,LineNumber
If ($line.Count -gt 1)
{
Write-Host "******* Warning ******"
Write-Host "Script is currently in 'WhatIf' mode; to make changes please remove '-WhatIf' parameter at line no. $($line[1].LineNumber)"
}
I'm sure there must be a better way to do this. Hope somebody can help.
Thanks
If you use the -Quiet switch on Select-String it will just return a boolean True/False, depending on whether it found a match or not.
-Quiet <SwitchParameter>
Returns a Boolean value (true or false), instead of a MatchInfo object. The value is "true" if the pattern is found; otherwise, the value is "false".
Required? false
Position? named
Default value Returns matches
Accept pipeline input? false
Accept wildcard characters? false
New to Powershell/Regex/EDI. Please no comments on why this shouldn't be done with regular expressions for EDI, I've seen the warnings, but have no choice.
What I need is most likely basic, but I need some help. I have to find all instances of a segment, and retrieve a specific element value from it. The text being searched will be read as one long string, with no CR/LF/etc.
Example data:
~SV1*HC:V2020*35*UN*1***1~DTP*472*D8*20120807~REF*6R*
~SV1*HC:V2100:LT*28.98*UN*1***1~DTP*472*D8*20120807~REF*6R*
~SV1*HC:92014*165*UN*1***1~DTP*472*D8*20120716~REF*6R*
I'm using the following command on another segment and it works just like i want, but it doesn't have to account for non-word characters either:
Select-String -pattern '~svd\*\w+\*(\d+|\d+\.\d+)\*' -input $string -AllMatches | %{$_.Matches} | %{$_.Groups[1]} | %{$_.Value}
Ideally, I would like to find an instance of "~SV1*", skip to the next asterisk, then read everything through the next asterisk. That way it doesn't matter what letter/number/character is in there, it skips it. In the data example above, I would want a return of 35, 28.98, 165. If not, then I can work with what I have, but matching on combinations of word/non-word characters is throwing me, since I don't know what order they may exist in. Everything else I've played with has continued on pulling the rest of the string, and not stopping properly.
If I can get it to do this, I'd be very happy:
~SV1*<skip this>*<get this>*<skip to next SV1>~SV1*<skip this>*<get this>*<skip to next SV1>
Lastly, the data being pulled is a money field, so it may or may not have a decimal present. If there is a cleaner way than (\d+|\d+.\d+), I'm all for it.
Thanks
A starting point, but you need to test it:
Select-String -pattern '(?<=~sv\d\*.*\*)(\d*\.?\d+)(?=\*un)' -input $string -AllMatches | %{$_.Matches} | % {$_.Groups[1]} | %{$_.Value}
Using your example data returns 35, 28.98, 165.
Use a pattern like this:
~sv\d\*[^*]*\*([^*]*)\*