I have a script that read a file name from path location and then he takes only the numbers and do something with them. could be more than one file in the path
An example for file:
Patch_1348968.vip
Patch_1348968_v1.vip
Patch_1348968_v2.Zip
It takes the number 1348968.
The code that do that is:
$compressedfiles = Get-ChildItem -path $FilesPath\* -Include "*.vip", "*.zip", "*cab"
foreach ($file in $compressedfiles) {
$PatchNumber = ""
$PatchNumber = $file.Name -replace '.*[-_](\d+).*', '$1'
more code....
}
The goal is to ignore (continue to the next file) while the pattern is not as patch_#########
An example to files I want to ignore:
patch-8.6.22 (DA GUI CU + 1351661 + 1344344).zip
Any idea how to do it?
Thanks
Your regex is too permissive to exclude the files that are not of interest.
Based on the examples, including in later comments, and your description, the following would work:
# Simulated Get-ChildItem output.
$compressedfiles = [System.IO.FileInfo[]] #(
'patch-8.6.22 (DA GUI CU + 1351661 + 1344344).zip',
'Patch_1348968.vip'
'Patch_1348968_v1.vip',
'Patch_1348968_v2.Zip',
'patch-1234567.zip',
'patch_7654321-V9.zip'
'patch-7654329-V10.zip',
'patch_42424242_abc453.zip',
'patch_42424243_copy#34.zip',
'Patch_1348968_copy.Zip'
)
foreach ($file in $compressedfiles) {
if ($file.Name -notmatch '^patch[-_](\d+)(?:[-_][\w]+#?\d*)?\.\w{3}$') {
Write-Verbose -Verbose "Skipping: $($file.Name)"
continue
}
$patchNumber = $Matches[1] # Get the number that the capture group matched.
$patchNumber # Sample output.
# ...
}
The above uses the -notmatch operator and the automatic $Matches variable instead of -replace, because the latter returns the input string as-is if its regex operand doesn't match (while you can compare the result to see if it is the same as the input string to infer whether -replace found at least one match, the above strikes me as conceptually clearer).
For an explanation of the regex and the ability to experiment with it, see this regex101.com page.
Use the -match operator to test if a string matches a given pattern. Extract the version number using a capture group (...) in the RegEx.
foreach ($file in $compressedfiles) {
if( $file.Name -match '^patch_(\d+)' ) {
$PatchNumber = $matches[1]
# more code....
}
}
The condition in the if statement evaluates to $true, when the pattern matches.
The pattern:
^ makes sure we match at the start of the file name, so names like foopatch_12345.zip won't match.
patch_ matches literally (case-insensitively by default)
( starts a capture group
\d+ matches one or more digits
) ends the capture group
For more information see the regex101 demo.
Using the automatic $matches variable, which contains the full match at index 0 and the matched values of any capture groups at subsequent indices, we extract the patch number. So $matches[1] is the value that matches the pattern \d+ within the parentheses.
Get-ChildItem -Path $path -Include "*patch*"
I'm making a script that collects all the subkeys from a specific location and converts the REG_BINARY keys to text, but for some reason I can't remove the duplicate results or sort them alphabetically.
PS: Unfortunately I need the solution to be executable from the command line.
Code:
$List = ForEach ($i In (Get-ChildItem -Path 'HKCU:SOFTWARE\000' -Recurse)) {$i.Property | ForEach-Object {([System.Text.Encoding]::Unicode.GetString($i.GetValue($_)))} | Select-String -Pattern ':'}; ForEach ($i In [char[]]'ABCDEFGHIJKLMNOPQRSTUVWXYZ') {$List = $($List -Replace("$i`:", "`n$i`:")).Trim()}; $List | Sort-Object -Unique
Test.reg:
Windows Registry Editor Version 5.00
[HKEY_CURRENT_USER\SOFTWARE\000\Test1]
"HistorySZ1"="Test1"
"HistoryBIN1"=hex:43,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,00,44,00,2e,00,\
7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,\
00,43,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,00,54,00,65,00,\
73,00,74,00,5c,00,42,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,\
00,54,00,65,00,73,00,74,00,5c,00,41,00,2e,00,7a,00,69,00,70,00,5c,00,00,00
[HKEY_CURRENT_USER\SOFTWARE\000\Test2]
"HistorySZ2"="Test2"
"HistoryBIN2"=hex:4f,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,00,44,00,2e,00,\
7a,00,69,00,70,00,5c,00,00,00,43,00,3a,00,5c,00,54,00,65,00,73,00,74,00,5c,\
00,43,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,44,00,3a,00,5c,00,54,00,65,00,\
73,00,74,00,5c,00,42,00,2e,00,7a,00,69,00,70,00,5c,00,00,00,41,00,3a,00,5c,\
00,54,00,65,00,73,00,74,00,5c,00,41,00,2e,00,7a,00,69,00,70,00,5c,00,00,00
The path strings that are encoded in your array of bytes are separated with NUL characters (code point 0x0).
Therefore, you need to split your string by this character into an array of individual paths, on which you can then perform operations such as Sort-Object:
You can represent a NUL character as "`0" in an expandable PowerShell string, or - inside a regex to pass to the -split operator - \0:
# Convert the byte array stored in the registry to a string.
$text = [System.Text.Encoding]::Unicode.GetString($i.GetValue($_))
# Split the string into an *array* of strings by NUL.
# Note: -ne '' filters out empty elements (the one at the end, in your case).
$list = $text -split '\0' -ne ''
# Sort the list.
$list | Sort-Object -Unique
After many attempts I discovered that it is necessary to use the Split command to make the lines break and thus be able to organize the result.
{$List = ($List -Replace("$i`:", "`n$i`:")) -Split("`n")}
I'm trying to remove duplicates from the Windows environment system path. It seems like the only way to do this is to split the path by semicolon and use sort-object with the -unique parameter or pipe to get-unique. However, it changes the sort order completely after that, and I want it to be the same order it was from the beginning, just minus the duplicates.
$RegPath = "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"
$hklm = [Microsoft.Win32.Registry]::LocalMachine
$RegKey = $hklm.OpenSubKey($regPath, $FALSE)
$OldPath = $regKey.GetValue("Path", "", [Microsoft.Win32.RegistryValueOptions]::DoNotExpandEnvironmentNames)
$SplitPath = $OldPath -split ';'
$NoDupesPath = ($SplitPath | Sort-Object | Get-Unique) -join ';'
I want $NoDupesPath to be the same order as $OldPath, minus the duplicates, but that's not happening when using Sort-Object. I also don't want any semicolons to show up at the very beginning or end of $NoDupesPath.
Inspire from this snippet I just threw together.
$s = 'a;b;c;b;b;a;a'
$a = $s -split ';'
$h = [ordered]#{}
# could use any value, or +1 to see count
$a | % {$h[$_] = $h[$_]+1}
"our hash table"
$h
"keys is unique and in order"
$h.Keys
$uniqueSameOrder = $h.Keys -join ';'
"uniqueSameOrder"
$uniqueSameOrder
Indeed, eliminating duplicates with Get-Unique (or, more directly, with Sort-Object -Unique) requires sorting the elements, which contradicts your requirements.
Update: Don Cruickshank's answer offers the simplest solution; conceptually speaking, the solution below is a manual (and therefore unnecessary) re-implementation of Select-Object -Unique.
Kory Gill's helpful answer contains all the ingredients for a solution; here's the full recipe:
$NoDupesPath = $OldPath -split ';' | ForEach-Object `
-Begin { $oht = [ordered] #{} } `
-Process { $oht[$_] = $true } `
-End { $oht.Keys -join ';' }
Due to pipeline use, this won't be the fastest solution, but it's concise and (hopefully) conceptually clear:
At the start of processing (the -Begin block) , $oht = [ordered] #{} creates an empty ordered hashtable (a hashtable whose keys reflect the order in which keys (entries) are created; PSv3+)
For each input path (the -Process block), if ($oht[$_]) { return } is a no-op if the input path at hand is already in the ordered hashtable, thereby skipping duplicates.
$oht[$_] = $true creates a hashtable entry for a path not previously seen; for a duplicate, the existing entry is simply updated, which amounts to a no-op, causing duplicates to be effectively ignored; after all paths have been processed, $oht.Keys therefore contains only unique paths, in input order.
At the end of processing (the -End block), $oht.Keys -join ';' outputs the unique paths - in input order - joined to form a single string with separator ;.
You can use Select-Object -Unique to get a unique collection and keep the order of the first occurrence of each item.
PS> $OldPath = "foo;bar;foo;baz;foo;bar;qux;baz;bar"
PS> $NoDupesPath = ($OldPath -split ';' | Select-Object -Unique) -join ';'
PS> $NoDupesPath
foo;bar;baz;qux
here's a solution that uses Group-Object. [grin] one thing that is not always apparent is that the resulting groups are in the order they are detected. that means you automatically get the original sequence in this situation ...
$OriSequence = 'a;f;c;b;b;f;a;a;c;s;r;v;q'
$SplitSequence = $OriSequence.Split(';')
$UniqueSequence = ($SplitSequence |
Group-Object).Name -join ';'
$OriSequence
$UniqueSequence
output ...
a;f;c;b;b;f;a;a;c;s;r;v;q
a;f;c;b;s;r;v;q