I need to be able to parse the folder path (which could vary) from an installed Windows service.
In PowerShell using
$serviceToRemove = Get-WmiObject -Class Win32_Service -Filter "name='Labelary'"
I can get the object, and if I do
$serviceToRemove.PathName
it gives
c:\program files\myapplicationname\mybinary.exe //rs/labelry
I need to get
c:\program files\myapplicationname
I've tried various combinations of splitting, but the path could vary or have spaces in it.
Is there a regex way of getting this portion of the path. The only guaranteed pattern is that I want the portion of the string which is before the wildcard *.exe . But I don't know how to express this in PowerShell. I tried \w*\.exe in a regex tester but this just returns the mybinary.exe.
Also various combinations of PowerShell or System.IO path tools seem to consider the //rs/labelry to be part of the path and thus not return the root correctly.
I would probably write it this way:
$pathName = Get-WmiObject Win32_Service -Filter "Name='Labelary'" |
Select-Object -ExpandProperty PathName |
Select-String '^"?(.+)\.exe' | ForEach-Object {
Split-Path $_.Matches[0].Groups[1].Value -Parent
}
Something like this should work:
$path = if ($serviceToRemove.PathName -match '[a-z]:\\.*?(?=\\[^\\]+\.exe)') {
$matches[0]
}
The regular expression matches a drive letter ([a-z]:\\) plus the shortest sequence of characters before a backslash followed by an executable name (\\[^\\]+\.exe). The positive lookahead assertion ((?=...)) ensures that the backslash and executable name are not included in the returned string.
Related
I have a folder including sub-folders in my Windows PC where I have multiple files of images with different dimensions with standard formatted names as shown below.
first-image-name.jpg
first-image-name-72x72.jpg
first-image-name-150x150.jpg
first-image-name-250x250.jpg
first-image-name-300x300.jpg
first-image-name-400x400.jpg
first-image-name-1024x1024.jpg
second-image-name.png
second-image-name-72x72.png
second-image-name-150x150.png
second-image-name-250x250.png
second-image-name-300x300.png
second-image-name-400x400.png
second-image-name-1024x1024.png
Now I want to delete all those image files that are of different sizes as shown in their name and should leave the original one only.
For that, I tried many queries as shared below but non of these are working...
Windows PowerShell:
Get-ChildItem $Path | Where{$_.Name -Match '.*[0-9]+x[0-9]+.\(jpg\|png\|jpeg\)$'} | Remove-Item
Windows CMD:
find -type f -regex '.*[0-9]+x[0-9]+.\(jpg\|png\|jpeg\)$' -delete
find -name '.*[0-9]+x[0-9]+.\(jpg\|png\|jpeg\)$' -delete
None of the above is working so let me know what I am doing wrong...??? Please remember I have to use it as recursive as I have many folders inside the main folder too.
The fourth bird has provided the crucial pointer in a comment:
You mistakenly \-escaped the following regex metacharacters in your regex, which causes them to be matched as literals: (, ), and |. Simply omitting the \ would work.
Conversely, you neglected to escape . as \., given that you want it to be interpreted literally.
However, I suggest the following optimization, which pre-filters the files of interest and then matches only against each pre-filtered file's base name (.BaseName), i.e. the name without its extension:
Get-ChildItem -Recurse $Path -Include *.jpg, *.jpeg, *.png |
Where-Object { $_.BaseName -match '-[0-9]+x[0-9]+$' } |
Remove-Item -WhatIf
Note: The -WhatIf common parameter in the command above previews the operation. Remove -WhatIf once you're sure the operation will do what you want.
Note:
The regex above needs no .* prefix, given that PowerShell's -match operator looks for substrings by default.
Using character class \d in lieu of [0-9] is an option, although \d technically also matches digits other than the ASCII-range characters 0 through 9, namely anything that the Unicode standard classifies as a digit.
While use of -Include, which (unlike -Filter) conveniently allows you to specify multiple PowerShell wildcard patterns, works as expected in combination with -Recurse, in the absence of -Recurse its behavior is counterintuitive:
See this answer for details.
I queried the registry to get a file path I am looking for. However, I need to go one directory lower to retrieve some file info I need. The pattern I am trying to match against is Officexx or OFFICExx. I can't seem to get the path I need.
Found path from registry: C:\Program Files\Microsoft Office
What I need is: C:\Program Files\Microsoft Office\Officexx
Code:
$base_install_path = "C:\Program Files\Microsoft Office";
$full_install_path = $base_install_path+'\Office[\d+.*]'
Write-Output $full_install_path;
This returns:
C:\Program Files\Microsoft Office\Office[\d+.*]
Desired output:
C:\Program Files\Microsoft Office\Office15
Not this could be any two digit # ^^
Building on Santiago Squarzon's helpful comment:
# Find all child directories matching the given wildcard pattern, if any.
Get-ChildItem -Directory -Path "$base_install_path\Office[0-9][0-9]*"
Unlike POSIX-compatible shells such as bash, PowerShell does not support automatic globbing of unquoted strings (pattern matching against file names, known as filename expansion) and instead requires explicit use of the Get-ChildItem or Get-Item cmdlets; e.g., the equivalent of bash command pattern='*.txt'; echo $pattern in PowerShell is $pattern='*.txt'; Get-ChildItem -Path $pattern
Note that objects describing the matching files or directories are output by these cmdlets; use their properties as needed, e.g. (Get-ChildItem $pattern).Name or (Get-ChildItem $pattern).FullName (full path). Use Get-ChildItem $pattern | Get-Member -Type Properties to see all available properties.
The -Path parameter of these cmdlets expects a PowerShell wildcard expression to perform the desired matching, and the expression in the command at the top matches exactly two digits ([0-9][0-9]), followed by zero or more characters (*), whatever they may be (potentially including additional digits).
Note: Only PowerShell's wildcard language - as accepted by the -Path and -Include / -Exclude parameters (and in many other contexts) - supports character ranges (e.g. [0-9] to match any decimal digit) and sets (e.g. [._] to match either . or _). By contrast, Get-ChildItem's -Filter parameter uses the wildcard language of the file-system APIs (as cmd.exe does), which does not support them, and additionally exhibits legacy quirks - see this answer for more information.
While PowerShell's wildcard character ranges and sets fundamentally work the same as in regexes (regular expressions, see below), regex-specific escape sequences such as \d are not supported, and you generally cannot quantify them; that is, something like [0-9] only ever matches exactly one digit.
Given that wildcard patterns support only one, non-specific duplication construct, namely the aforementioned *, matching a specific range of digits - such as 1 or 2 at most or a specific count - such as exactly two - requires post-filtering based on a regex (which is what you tried to use):
# Find all child directories matching the given regex, if any.
# Matches 'Office' at the start of the name (^),
# followed by 1 or 2 ({1,2}) digits (\d),
# followed by at least non-digit (\D), if any (?)
Get-ChildItem -Directory -LiteralPath $base_install_path |
Where-Object Name -match '^Office\d{1,2}\D?'
As for what you tried:
[\d+.*] is a regex, but you probably meant \d+.*, i.e. one or more (+) digits (\d) followed by zero more (*) characters, whatever they may be (.)
Inside a character-range/set expression ([...]), +, . and * are used verbatim, i.e. they are not metacharacters and match literal . and * characters.
Get-ChildItem -Path 'C:\Program Files\Microsoft Office\' -Directory |
Where-Object { $_.Name -match 'Office\d+' }
In your regex, [] is a character class which means [\d+.*] is not "one or more numbers" it's "a backslash OR d OR plus OR dot OR asterisk".
PS C:\> "d+\" -match "[\d+]"
True
Not what you were looking for.
I want to CD into a folder that begins with the word "Patch", followed by several other digits. All I care about is that it begins with the word "Patch"
How can I change my directory using a regex in powershell?
This is what I have at the moment and it doesn't work. Am I on the right track though?
$FolderPath = "^Patch[0-9]+$"
cd "C:\Test\" + $FolderPath
To put you in the right direction:
Use Get-ChildItem and only get the directories matching your regex, something like:
$matchingItem = Get-ChildItem "C:\Test" -Directory | ?{ $_.Name -match $FolderPath } | select -First 1
Now you can cd to the matching directory.
You don't really need the regex. Wildcard blobbing will handle that:
cd c:\test\patch[0-9]*
I'm trying to write a script which reads file paths from a set of registry keys, but these paths have environment variables like %SystemRoot% in. I know you can usually look up the value of these using:
$env:SystemRoot
for example. However, if PS receives a string such as "%SystemRoot%\System32\Winevt\Logs\DebugChannel.etl", how can this get converted to the full path I need, i.e. "C:\Windows\System32\Winevt\Logs\DebugChannel.etl"?
I've tried using a regex -replace to convert the %% format to the $env: format:
$_.FileName -replace "%(\w*)%\\", "`$env:`${1}\"
But this just results in the string:
$env:SystemRoot\System32\Winevt\Logs\DebugChannel.etl
And I'm not sure how to get PowerShell to actually evaluate the "$env:SystemRoot" part. I presume there is a more sensible way of doing this anyway!
The full code I'm using to get the registry values and produce the above result is
Get-ChildItem HKLM:\SYSTEM\CurrentControlSet\Control\WMI\Autologger | Get-ItemProperty -Name "FileName" -ErrorAction SilentlyContinue | foreach { if($_.FileName) { $_.FileName -replace "%(\w*)%\\", "`$env:`${1}\" } }
Thanks.
Using .net. Try this:
[System.Environment]::ExpandEnvironmentVariables("%SystemRoot%\System32\Winevt\Logs\DebugChannel.etl")
In a deployment szenario, I need to rename config files. There are config files for every environment (Dev.Test, Dev.Prod, Integration, Prod). For example a web.config would be called web.Dev.Test.config if it was for the Dev.Test environment. On the target machine, I need to rename the files back to their original name (i.e. from web.Dev.Test.config to web.config) with Powershell.
$test = "web.Dev.Prod.config"
$environment = $test | Select-String -Pattern ".*\.(?<environment>(Dev.Test|Dev.Prod|Prod|Integration))\.config" | select -expand Matches | foreach {$_.groups["environment"].value}
if ($test -match "Dev.Prod")
{
$environment = "Dev.Prod"
}
$environment
$newFileName = $test.Remove($test.IndexOf($environment),$environment.Length + 1)
$newFileName
The problem I have with this is, that the Regex does not find the Dev.Prod evironment, but returns Prod instead. This is why I introduced the if statement. I was wondering if there was a more elegant way of renaming the files with Powershell.
Watch out for greedy matching. Modify your regex that starts ".*\.(?" to ".*?\.(?".