powershell -match 2 different parts of one URL and get them - powershell

I want to get to different parts of a URL that I retrieve with PowerShell (https://repo.anaconda.com/archive/Anaconda3-2022.05-Windows-x86_64.exe) the -match returns true, the first match is a date that I can get correct and the second part is the name of the file (Anaconda3-2022.05-Windows-x86_64.exe) that I can not get it,
can someone help me with it, thanks in advance
here is my script
$Uri = 'https://www.anaconda.com/products/distribution#Downloads'
( $web = Invoke-WebRequest -Uri $uri)
( $downloadurl=$web.Links |Where-Object href -Like "*_64.exe" |Select-Object -First 1 | Select-Object -expand href )
( $downloadurl -match "(\d\d\d\d.\d{1,2}).*(Anaconda*.exe)" )
($latestversion = "$($Matches[1])")
($FileName = "$($Matches[2])")

There's an easier way to parse the filename than building a regex.
First, split on slash / to get an array of strings. Since the filename is the last one, use [-1] indexer to get it. Like so,
$downloadurl.split('/')[-1]
Anaconda3-2022.05-Windows-x86_64.exe
Now that you have a string with filenamme only, split on dash -:
$downloadurl.split('/')[-1].split('-')
Anaconda3
2022.05
Windows
x86_64.exe
Finding the version number is then simple indexing to 2nd element of the resulting array, and the filename is already availale from the previous split operation.

Related

Get the files whose date in the filename is greater than some specific date using Powershell script

I have a specific date "2021/11/28", i want the list of files from example filenames(below) whose file name is greater than 2021/11/28. remember not the creation time of the file names.
"test_20211122_aba.*"
"abc_20211129_efg.*"
"hij_20211112_lmn.*"
"opq_20211130_rst.*"
I'm expecting to get
"abc_20211129_efg.*"
"opq_20211130_rst.*"
Really appreciate your help.
You don't strictly need to parse your strings into dates ([datetime] instances): Because the date strings embedded in your file names are in a format where their lexical sorting is equivalent to chronological sorting, you can compare the string representations directly:
# Simulate output from a Get-ChildItem call.
$files = [System.IO.FileInfo[]] (
"test_20211122_aba1.txt",
"abc_20211129_efg2.txt",
"hij_20211112_lmn3.txt",
"hij_20211112_lmn4.txt",
"opq_20211130_rst5.txt"
)
# Filter the array of files.
$resultFiles =
$files | Where-Object {
$_.Name -match '(?:^|.*\D)(\d{8})(?:\D.*|$)' -and
$Matches[1] -gt ('2021/11/28"' -replace '/')
}
# Print the names of the filtered files.
$resultFiles.Name
$_.Name -match '(?:^|.*\D)(\d{8})(?:\D.*|$)' looks for (the last) run of exactly 8 digits in each file name via a capture group ((...)), reflected in the automatic $Matches variable's entry with index 1 ($Matches[1]) afterwards, if found.
'2021/11/28"' -replace '/' removes all / characters from the input string, to make the format of the date strings the same. For brevity, the solution above performs this replacement in each loop operation. In practice, you would perform it once, before the loop, and assign the result to a variable for use inside the loop.

Get-GPOReport and Search For Matched Name Value

I'm trying to use the PowerShell command 'Get-GPOReport' to get GPO information in XML string format so I can search it for sub-Element values with unknown and different Element tag names (I don't think XML Object format will work for me, so I didn't perform a cast with "[xml]"), but I haven't been able to parse the XML output so that I can grab the line or two after a desired "Name" Element line that matches the text I'm searching for.
After, I have been trying to use 'Select-String' or 'Select-XML' with XPath (formatting is unclear and I don't know if I can use a format for various policy record locations) to match text and grab a value, but I haven't had any luck.
Also, if anyone know how to search for GPMC GUI names (i.e. "Enforce password history") instead of needing to first locate back-end equivalent names to search for (i.e. "PasswordHistorySize"), that would also be more helpful.
The following initial code is the part that works:
$String = "PasswordHistorySize" # This is an example string, as I will search for various strings eventually from a file, but I'm not sure if I could search for equivalent Group Policy GUI text "Enforce password history", if anyone knows how to do that.
$CurrentGPOReport = Get-GPOReport -Guid $GPO.Id -ReportType Xml -Domain $Domain -Server $NearestDC
If ($CurrentGPOReport -match $String)
{
Write-Host "Policy Found: ""$($String)""" -Foregroundcolor Green
#
#
# The following code is what I've tried to use to get value data, without any luck:
#
$ValueLine1 = $($CurrentGPOReport | Select-String -Pattern $String -Context 0,2)
$Value = $($Pattern = ">(.*?)</" ; [regex]::match($ValueLine1, $Pattern).Groups[1].Value)
}
I've been looking at this since yesterday and didn't understand why Select-String wasn't working, and I figured it out today... The report is stored as a multi-line string, rather than an array of strings. You could do a -match against it for the value, but Select-String doesn't like the multi-line formatting it seems. If you -split '[\r\n]+' on it you can get Select-String to find your string.
If you want to use RegEx to just snipe the setting value you can do it with a multi-line regex search like this:
$String = "PasswordHistorySize" # This is an example string, as I will search for various strings eventually from a file, but I'm not sure if I could search for equivalent Group Policy GUI text "Enforce password history", if anyone knows how to do that.
$CurrentGPOReport = Get-GPOReport -Guid $GPO.Id -ReportType Xml -Domain $Domain -Server $NearestDC
$RegEx = '(?s)' + [RegEx]::Escape($String) + '.+?Setting.*?>(.*?)<'
If($CurrentGPOReport -match $RegEx)
{
Write-Host "Policy Found: ""$String""" -Foregroundcolor Green
$Value = $Matches[1]
}
I'm not sure how to match the GPMC name, sorry about that, but this should get you closer to your goals.
Edit: To try and get every setting separated out into it's own chunk of text and not just work on that one policy I had to alter my RegEx a bit. This one's a little more messy with the output, but can be cleaned up simply enough I think. This will split a GPO into individual settings:
$Policies = $CurrentGPOReport -split '(\<(q\d+:.+?>).+?\<(?:\/\2))' | Where { $_ -match ':Name' }
That will get you a collection of things that look like this:
<q1:Account>
<q1:Name>PasswordHistorySize</q1:Name>
<q1:SettingNumber>21</q1:SettingNumber>
<q1:Type>Password</q1:Type>
</q1:Account>
From there you just have to filter for whatever setting you're looking for.
I have tried this with XPath, as you'll have more control navigating in the XML nodes:
[string]$SearchQuery = "user"
[xml]$Xml = Get-GPOReport -Name "Default Domain Policy" -ReportType xml
[array]$Nodes = Select-Xml -Xml $Xml -Namespace #{gpo="http://www.microsoft.com/GroupPolicy/Settings"} -XPath "//*"
$Nodes | Where-Object -FilterScript {$_.Node.'#text' -match $SearchQuery} | ForEach-Object -Process {
$_.Name #Name of the found node
$_.Node.'#text' #text in between the tags
$_.Node.ParentNode.ChildNodes.LocalName #other nodes on the same level
}
After testing we found that in the XML output of the Get-GPOReport cmdlet, the setting names does not always match that of the HTML output. For example: "Log on as a service" is found as "SeServiceLogonRight" in the XML output.

Get-IniContent Help Getting Specific Sections with Wildcards

I have multiple INI files that I have to update regularly as new lines are being added in our call center. I can use the get-inicontent script to get the values I'm trying to compare however The ID section sometimes has spaces and sometimes doesn't also and probably the biggest trouble I'm having is scaling. Each INI can have multiple sections labeled the same thing but with a number at the end. I need the ID for each ACD section. See below for an example of my file and what I use to get the value for one ACD. The below script returns each value but it requires individual query for each and note the spaces for those that have tabs. I'd like to figure out a way to return all ACD ID results that ignores the extra spaces. Any help would be greatly appreciated.
INI File:
[ACD1]
ID=1001
[ACD2]
ID=1002
[ACD3]
ID=1003
[Extension1]
ID=50001
[Extension2]
ID=50002
PS Script:
$FileContents = Get-IniContent "C:\Temp\ScriptTest\CTISetupTest.ini"
$FileContents.ACD1.ID
$FileContents.ACD2.' ID'
$FileContents.ACD3.' ID'
Expected Results would be:
1001
1002
1003
Just loop over the keys starting with ACD and then loop over the subkeys ending in ID.
$INI = Get-IniContent 'C:\Temp\ScriptTest\CTISetupTest.ini'
$ACDKeys = ($INI).keys | Where-Object {$_ -like 'acd*'}
foreach ($ACDKey in $ACDKeys) {
$IDKeys = $INI[$ACDKey].Keys | Where-Object {$_ -like '*ID'}
foreach ($IDKey in $IDKeys){
$INI[$ACDKey][$IDKey]
}
}

Search string in text file and display until the next delimiter

I have got a file "servers.txt" :
[Server1]
Value_A
Value_B
Value_C
[Server2]
Value_A
[Server3]
Value_A
Value_B
Value_C
Value_D
===
I need to search into this file and display the server line + all his values.
Something like :
$search = "server3"
gc servers.txt | Select-String -Pattern $search and diplay until the next "["
(I can't tell for example, display the line+1, because the values are different, sometimes there are only 3, sometimes 1, etc.)
Thanks a lot!
How about:
$search = "server3"
(gc servers.txt -Delimiter '[') -like "$search]*" -replace '^','[' -replace '\s*\[$'
Cleaner solution (I think):
(gc servers.txt -raw) -split '\r\n(?=\[)' -like "?$search]*"
Looks like your delimiter is a blank line. How about reading the file and processing it so the first line is server name, all the following lines until a blank are an array of data, and then on blank lines it outputs a custom object with the server name and array of data as properties, and creating an array of those object?
Hm, that's confusing, and I wrote it. Let me post code, and then explain it.
$Server = ""
$Data = #()
$Collection = #()
Switch(GC C:\temp\test.txt){
{[String]::IsNullOrEmpty($Server) -and !([String]::IsNullOrWhiteSpace($_))}{$Server = $_;Continue}
{!([String]::IsNullOrEmpty($Server)) -and !([String]::IsNullOrEmpty($_))}{$Data+=$_;Continue}
{[String]::IsNullOrEmpty($_)}{$Collection+=[PSCustomObject]#{Server=$Server;Data=$Data};Remove-Variable Server; $Data=#()}
}
If(!([String]::IsNullOrEmpty($Server))){$Collection+=[PSCustomObject]#{Server=$Server;Data=$Data};Remove-Variable Server; $Data=#()}
Ok, it starts out by defining variables as either empty strings or arrays.
Then it processes each line, and performs one of three actions depending on the situation. The first line of the switch reads the text file, and processes it line by line. The first option in the Switch basically reads:
If there is nothing stored in the $Server variable, and the current line is not blank, then $Server = Current Line. Continue to the next line.
The second option is:
If $Server is not blank, and the current line is not blank, add this line to the array $Data. Continue to the next line.
The last option for the Switch is:
If the current line is blank, then this is the end of the current record. Create a custom object with two properties. The first property is named Server, and the value is whatever is in $Server. The second property is named Data, and the value is whatever is in $Data. Then remove the $server variable, and reset $Data to an empty array.
After the switch it checks to see if $Server still has data, and outputs one last object if it does. I do this in case there is no blank line at the end of the last record, just as cleanup.
What you are left with is $Collection being an array of objects that looks something like this:
Server Data
------ ----
[Server1] {Value_A , Value_B , Value_C}
[Server2] {Value_A}
[Server3] {Value_A , Value_B , Value_C , Value_D}

Get last element of pipeline in powershell

This might be weird, but stay with me.
I want to get only the last element of a piped result to be assigned to a varaiable.
I know how I would do this in "regular" code of course, but since this must be a one-liner.
More specifically, I'm interested in getting the file extension when getting the result from an FTP request ListDirectoryDetails.
Since this is done within a string expansion, I can't figure out the proper code.
Currently I'm getting the last 3 hars, but that is real nasty.
New-Object PSObject -Property #{
LastWriteTime = [DateTime]::ParseExact($tempDate, "MMM dd HH:mm",[System.Globalization.CultureInfo]::InvariantCulture)
Type = $(if([int]$tempSize -eq 0) { "Directory" } else { $tempName.SubString($tempName.length-3,3) })
Name = $tempName
Size = [int]$tempSize
}
My idea was doing something similar to
$tempName.Split(".") | ? {$_ -eq $input[$input.Length-1]}
that is, iterate over all, but only take out where the element I'm looking at is the last one of the input-array.
What am I missing ?
A few ways to do this:
$name = 'c:\temp\aaa.bbb.ccc'
# way 1
$name.Split('.') | Select-Object -Last 1
# way 2
[System.IO.Path]::GetExtension($name)
# or if the dot is not needed
[System.IO.Path]::GetExtension($name).TrimStart('.')
In general, getting the last element in the pipeline would be done using Select -Last 1 as Roman suggests above. However, an alternate and easier way to do this if the input is a simple array is to use array slicing e.g.:
PS> $name = "c:\temp\aaa.bbb.txt"
PS> $name.Split('.')[-1]
txt
Was your intent to get the file's extension or basename? Because it seems that the Type property already contains the extension for the filename.