Select-String Doesn't Show All Matches With Get-AppxPackage - windows

I get all the packages installed on my PC using Get-AppxPackage and I'm trying to find all the matches in that with N lines before and after using Select-String.
However, the select string is only showing a matches as single line and it's not showing all the matches either. This only happens when I pipe the output from Get-AppxPackage and not if I write it to a file and then do cat <filename> | select-string ....
As you can see in the example below the two results of using pipe and cat. I'm interested in results like from cat i.e. detailed info about the app.
So what am I doing wrong here? Why is the output different?
Example (everyone should have MS Edge so I'll use that as an example) :
PS > Get-AppxPackage | Select-String -pattern 'edge' -context 3, 3 -allmatches
Microsoft.Windows.StartMenuExperienceHost_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
Microsoft.Windows.Cortana_1.13.0.18362_neutral_neutral_cw5n1h2txyewy
Microsoft.AAD.BrokerPlugin_1000.18362.329.0_neutral_neutral_cw5n1h2txyewy
> Microsoft.MicrosoftEdge_44.18362.329.0_neutral__8wekyb3d8bbwe
Microsoft.Windows.CloudExperienceHost_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
Microsoft.Windows.ContentDeliveryManager_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
Windows.CBSPreview_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
Microsoft.Windows.Apprep.ChxApp_1000.18362.329.0_neutral_neutral_cw5n1h2txyewy
Microsoft.Win32WebViewHost_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
Microsoft.PPIProjection_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
> Microsoft.MicrosoftEdgeDevToolsClient_1000.18362.329.0_neutral_neutral_8wekyb3d8bbwe
Microsoft.LockApp_10.0.18362.329_neutral__cw5n1h2txyewy
> Microsoft.EdgeDevtoolsPlugin_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
Microsoft.ECApp_10.0.18362.329_neutral__8wekyb3d8bbwe
Microsoft.CredDialogHost_10.0.18362.329_neutral__cw5n1h2txyewy
Microsoft.BioEnrollment_10.0.18362.329_neutral__cw5n1h2txyewy
PS > cat .\appx-packages.txt | select-string -pattern 'edge' -context 3, 3 -allmatches
SignatureKind : System
Status : Ok
> Name : Microsoft.MicrosoftEdge
Publisher : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture : Neutral
ResourceId :
Version : 44.18362.329.0
> PackageFullName : Microsoft.MicrosoftEdge_44.18362.329.0_neutral__8wekyb3d8bbwe
> InstallLocation : C:\Windows\SystemApps\Microsoft.MicrosoftEdge_8wekyb3d8bbwe
IsFramework : False
> PackageFamilyName : Microsoft.MicrosoftEdge_8wekyb3d8bbwe
PublisherId : 8wekyb3d8bbwe
IsResourcePackage : False
IsBundle : False
SignatureKind : System
Status : Ok
> Name : Microsoft.MicrosoftEdgeDevToolsClient
Publisher : CN=Microsoft Corporation, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture : Neutral
ResourceId : neutral
Version : 1000.18362.329.0
> PackageFullName : Microsoft.MicrosoftEdgeDevToolsClient_1000.18362.329.0_neutral_neutral_8wekyb3d8bbwe
> InstallLocation : C:\Windows\SystemApps\Microsoft.MicrosoftEdgeDevToolsClient_8wekyb3d8bbwe
IsFramework : False
> PackageFamilyName : Microsoft.MicrosoftEdgeDevToolsClient_8wekyb3d8bbwe
PublisherId : 8wekyb3d8bbwe
IsResourcePackage : False
IsBundle : False
SignatureKind : System
Status : Ok
> Name : Microsoft.EdgeDevtoolsPlugin
Publisher : CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
Architecture : Neutral
ResourceId : neutral
Version : 10.0.18362.329
> PackageFullName : Microsoft.EdgeDevtoolsPlugin_10.0.18362.329_neutral_neutral_cw5n1h2txyewy
> InstallLocation : C:\Windows\SystemApps\Microsoft.EdgeDevtoolsPlugin_cw5n1h2txyewy
IsFramework : False
> PackageFamilyName : Microsoft.EdgeDevtoolsPlugin_cw5n1h2txyewy
PublisherId : cw5n1h2txyewy
IsResourcePackage : False
IsBundle : False

tl;dr
As of PowerShell v7.3, to make Select-String search non-string input by the same rich string representation you'd see in the console (terminal), you must pipe the input to oss first:
Get-AppxPackage | oss | Select-String -Pattern 'edge' -Context 3, 3
Note:
This intermediate step should not be necessary, as discussed in GitHub issue #10726.
In fact it already isn't necessary when you're piping non-string input to an external program instead, say findstr.exe, because PowerShell then implicitly uses the rich string representation, because it has to send the input as (meaningful) strings.[1]
As an aside: Select-String is convenient for searching through lines of text and - with oss - for searching the formatted string representations of objects, without having to know or care about their specific structure.
However, if you do know the structure, using OO techniques via different cmdlets, such as Where-Object and Select-Object is more robust. For instance, the following filters packages by their .Name property and then selects only the properties of interest:
# Note: Get-AppXPackage *edge* would obviate the need for Where-Object
Get-AppXPackage |
Where-Object Name -like *edge* |
Select-Object Name, Version
Background information:
Select-String, when given input other than strings, uses simple .ToString() stringification[2] on each input object before looking for the given pattern.
In your case, the [Microsoft.Windows.Appx.PackageManager.Commands.AppxPackage] instances output by Get-AppXPackage stringify to the full package names (e.g., Microsoft.MicrosoftEdge_44.18362.387.0_neutral__8wekyb3d8bbwe), which explains your output.
In order to make Select-String search the for-display string representations of objects - as they would print to the console and as they would appear in a file saved to with > / Out-File (cat is Out-File's built-in alias on Windows) - you must, surprisingly, use Out-String -Stream as an intermediate pipeline segment; since PowerShell v5, if memory serves, you can use built-in wrapper function oss for brevity:
# oss is a built-in wrapper function for Out-String -Stream
Get-AppxPackage | oss | Select-String -Pattern 'edge' -Context 3, 3
Out-String uses PowerShell's formatting system to produce human-friendly display representations of the input objects, the same way that default console output, the Format-* cmdlets, and > / Out-File do.
-Stream causes the output lines to be sent through the pipeline one by one, so that Select-String can match individual lines.
Given that the solution is both non-obvious and cumbersome, it would be nice if Select-String directly supported this behavior, ideally by default, but at least on an opt-in basis via a switch parameter - see feature request #10726 on GitHub - up-vote the proposal there if you agree.
[1] As of v7.3, PowerShell only "speaks text" when communicating with external programs, so it has to create a string representation of non-string objects when passing data to them: see this answer. While it makes sense to default to the for-display string representation of such objects, note that this representation isn't meant for programmatic processing; for the latter, it's best to explicitly output a structured text format, such as JSON via ConvertTo-Json.
[2] More accurately, .psobject.ToString() is called, either as-is, or - if the object's ToString method supports an IFormatProvider-typed argument - as .psobject.ToString([cultureinfo]::InvariantCulture) so as to obtain a culture-invariant representation - see this answer for more information.

Related

Powershell - Get-Process's ProcessName is truncated on macOS

I'm using Powershell 7.2.5 on macOS 12.5.
I had Google Chrome open and ran Get-Process google*, and got truncated ProcessName:
gps google*
NPM(K) PM(M) WS(M) CPU(s) Id SI ProcessName
------ ----- ----- ------ -- -- -----------
0 0.00 626.36 5,529.29 33973 1 Google Chrome
0 0.00 124.94 2,870.73 33993 1 Google Chrome H
Google Chrome H should be Google Chrome Helper or something longer.
gps | sort-object {$_.ProcessName.length} showed that all processnames are truncated to 15 chars.
How do I make Get-Process output without truncation?
I read the help for Get-Process and tried to pipe the output to Format-Custom and Format-List ProcessName -Force, and none of these tricks worked.
Unfortunately, this is not a formatting problem:
At least up to the .NET version underlying PowerShell Core 7.3.0-preview.6, .NET , .NET 7.0.0-preview.6.22324.4, the .ProcessName property value is limited to 15 chars.
This is a known problem, and there is a known fix, but no one has stepped up to implement it yet - see GitHub issue #52860
A - computationally expensive - workaround is to use a call to the native ps utility, via a calculated property:
Get-Process google* |
Select-Object Id,
#{ n='ProcessName'; e={ Split-Path -Leaf (ps -p $_.Id -o comm=) }
Note: The above reports just the process ID and the (full) process name.
More work is needed if you want the same display columns as you would get by default, only with the full process names:
Get-Process google* | ForEach-Object {
$copy = $_ | Select-Object *
$copy.ProcessName = Split-Path -Leaf (ps -p $_.Id -o comm=)
$copy.pstypenames.Insert(0, 'System.Diagnostics.Process')
$copy
}

How to column print only certain row parts of a list in powershell?

I have tried various ways to format the output from a poweshell command and would like to print only some of the row items in the list as part of a a column in one line.
Perhaps more easy to illustrate:
# I want the output from:
Get-CimInstance Win32_OperatingSystem | select Caption,Version,OSArchitecture,InstallDate | fl
Caption : Microsoft HAL 9000
Version : 6.3.9000
OSArchitecture : 64-bit
InstallDate : 2018-08-16 00:50:01
# To look like this:
Microsoft HAL 9000 (6.3.9000) 64-bit [2018-08-16 00:50:01]
How can this be easily accomplished?
(Coincidentally I want all the rows in this case, but a more general answer may be more useful, if it also include rows that we don't want.)
PowerShell usually returns objects and outputs a string representation of it to the host. You want a custom string format output to the host. You can achieve that in various ways, however the fastest way and my recommendation would be to use the -f operator.
$OS = Get-CimInstance Win32_OperatingSystem
'{0} ({1}) {2} [{3}]' -f $OS.Caption, $OS.Version, $OS.OSArchitecture, $OS.InstallDate
With here-strings use can do the same with multi-line.
$OS = Get-CimInstance Win32_OperatingSystem
#'
My OS is {0} {1})
Architecture --> {2}
Installation Date: [{3}]
'# -f $OS.Caption, $OS.Version, $OS.OSArchitecture, $OS.InstallDate
However, you should work with objects as much as - and as long as it is possible.
I would belive this should work for you:
$temp = (Get-CimInstance Win32_OperatingSystem | Select-Object Caption, Version, OSArchitecture,InstallDate)
The Select-Object makes sure that you get the desired properties. Having a variable with all the details in it, we can concatenate it like this:
"$($temp.Caption) ($($temp.version)) $($temp.OSArchitecture) [$($temp.InstallDate.ToString("yyyy-MM-dd hh:mm:ss"))]"
Simply use Format-Table instead of Format-List. They both support a list of properties you want to see. So if you don't want all columns, list the ones you want.
# 'default' properties in a table
Get-CimInstance Win32_OperatingSystem | ft
# only some properties in a table
Get-CimInstance Win32_OperatingSystem | ft Caption, OSArchitecture
# without table headers
Get-CimInstance Win32_OperatingSystem | ft Caption, OSArchitecture -HideTableHeaders
# all properties in a list (because there are too many for a table)
Get-CimInstance Win32_OperatingSystem | fl *

Check if program with specific version is installed

I'm trying to create a very simple script which would check if the a specific program is installed and if so return the version number for that program.
I've been able to get to the point where I'm running the script and able to return a binary value if a program is installed or not but not sure how to return the version number for that installed program.
What I will post will be just what I'm doing to return if program is installed, and need help in then attaining the version number.
function Check_Program_Installed {
$my_check = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* |
Select-Object DisplayName, DisplayVersion, InstallDate |
Format-Table -AutoSize |
Out-String
# Check if Google Chrome is installed
$my_check -Match "Google Chrome"
}
Check_Program_Installed
If you want that function to look for a specific installed program instead of returning a (table) formatted string, then you could simply do:
function Check_Program_Installed {
[CmdletBinding()]
Param(
[Parameter(Position = 0, Mandatory=$true, ValueFromPipeline = $true)]
$Name
)
$app = Get-ItemProperty -Path "HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*" |
Where-Object { $_.DisplayName -match $Name } |
Select-Object DisplayName, DisplayVersion, InstallDate, Version
if ($app) {
return $app.DisplayVersion
}
}
Check_Program_Installed "Google Chrome"
This will return $null when not found, or the version as string like 70.0.3538.67
Instead of doing the match after formatting the table, you could add a where to select the result you need beforehand and then obtain the DisplayVersion directly from that object. You could clean this up more to do exactly what you need, but here is your code modified to retrieve and display the number if the application is found. Try switching to a bad name to see the else result:
function Get-ApplicationVersion {
$applicationName = "Google Chrome"
$my_check = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, InstallDate | Where -Property DisplayName -Match $applicationName
$versionNumber = $my_check.DisplayVersion
if ($my_check) {
$versionNumber
}
else {
write-warning "Application not found"
}
}
Get-ApplicationVersion
EDITED: Renamed function name from Check_Program_Installed to use PS common verb Get, per suggestion.
function Get-InstalledProgram {
Param (
$ProgramName
)
$UninstallKeys = Get-ItemProperty HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
if ( $ProgramName )
{
$UninstallKeys | Where-Object -Property DisplayName -Match -Value $ProgramName | Select-Object DisplayName, DisplayVersion, InstallDate
}
else
{
$UninstallKeys | Select-Object DisplayName, DisplayVersion, InstallDate
}
}
If you wanted to see all the programs, then you don't have to add a parameter. Just pipe its output to Format-Table. Format-Table does some weird just where the items are no longer the objects you're expecting, but table objects. Here is how I would handle that:
Get-InstalledProgram | Format-Table -Autosize
If you want to search for a program, add a parameter. You'll see above I added a parameter for ProgramName. It will match this term to the registry key's DisplayName.
PS C:\> Get-InstalledProgram -ProgramName Java
DisplayName DisplayVersion InstallDate
----------- -------------- -----------
Java 8 Update 181 8.0.1810.25 20180725
Java Auto Updater 2.8.181.13 20180925
If you wanted to just get the version, I would recommend just piping your output to Select-Object -ExpandProperty DisplayVersion
PS C:\> Get-InstalledProgram -ProgramName 'Java 8' | Select-Object -ExpandProperty DisplayVersion
8.0.1810.25
tl;dr
In Windows PowerShell[1] v5.1+, use the following (searches among both 32-bit and 64-bit installed programs, as shown in Control Panel):
Get-Package -ProviderName Programs -IncludeWindowsInstaller '*Google Chrome*' |
ForEach-Object Version
Note: The 32-bit-only HKEY_LOCAL_MACHINE:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall registry key may have more specific entries than what Control Panel shows - I'm unclear on why, but perhaps the composite view in Control Panel is sufficient.
Applied to your example:
PS> (Get-Package -ProviderName Programs -IncludeWindowsInstaller '*Google Chrome*').Version
70.0.3538.67
As for what you tried:
Since you're checking the Wow6432Node registry key branch specifically, you're checking installed 32-bit programs only.
As such, a better name for your function would be Check_32BitProgram_Installed or, more in line with with the function's intent, using an approved PowerShell verb, Get-32BitProgramVersion.
Alternatively, name, the function Get-ProgramVersion and look in both the 32-bit and 64-bit locations and process the results as shown in Theo's and Kevin M. Lapio's and Shawn Esterman's helpful answers:
Get-ItemProperty HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\*,
HKLM:\Software\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\*
In line with the generic title of your question, the above is a solution that essentially searches the list of installed applications you would see in Control Panel > Programs > Programs and Features (appwiz.cpl), which covers both 32-bit and 64-bit applications:
Windows PowerShell v5.1 comes with the PackageManagement module and a Programs package provider[1] that allows inspecting installed programs via the Get-Package cmdlet; in PSv3 and PSv4, it is possible to manually install it.
To list installed programs (shown with abridged sample output):
PS> Get-Package -ProviderName Programs -IncludeWindowsInstaller
Name Version Source ProviderName
---- ------- ------ ------------
Git version 2.18.0 2.18.0 Programs
Microsoft Azure Compute Emu... 2.9.8699.20 Programs
Microsoft Azure Authoring T... 2.9.8699.20 Programs
# ...
The output objects are of type [Microsoft.PackageManagement.Packaging.SoftwareIdentity], which have .Name and .Version properties, which enables the solutions above.
The Programs package provider supports two dynamic options (options specific to that provider):
-IncludeWindowsInstaller is needed to make the list of programs reported match what Control Panel shows.
-IncludeSystemComponent, by contrast, reports components that do not show in Control Panel.
[1] Unfortunately, the underlying Programs package provider is not available in PowerShell Core on Windows as of v7.0 - and I'm unclear on whether that is a not-yet situation or whether it will never be - see GitHub issue #13225.

Oracle Listener Name without checking listener.ora file for windows

I need to find the oracle database listener name in windows through script without reading any file present in oracle home directory,may be through services or processes in windows like we are able to get it in Linux as:-
ps -ef|grep tnslsnr
which gives an output like
root 18489 14155 0 11:22 pts/1 00:00:00 grep tnslsnr
oracle 19857 1 0 Jul22 ? 00:00:50 /opt/app/product/12.1.0/dbhome_1/bin/tnslsnr LISTENER -inherit
Here listener name is LISTENER i.e. the name after "/opt/app/product/12.1.0/dbhome_1/bin/tnslsnr"
Is it possible either through powershell script or batch script ?
The Oracle "lsnrctl"/"netca" tools will create the listener service short name/alias with fairly predictable nomenclature (very least should have the words "Oracle" and "LISTENER" in there even if u specify default/no listener name), and service names are readily available from the Registry. With PowerShell, you could do something like:
Get-Service | Where-Object Name -Match "^\w*<listener name>\w*$" | Select Name
Get-Service | Where-Object Name -Match "^\w*<instance key>\w*<listener name>\w*$" | Select Name
Get-Service | Where-Object Name -Match "^\w*<instance key>\w*LISTENER\w*$" | Select Name
Get-Service | Where-Object Name -Match "^\w*Oracle\w*LISTENER\w*$" | Select Name
Where:
"<listener name>" --> listener name used when created using "netca" or "lsnrctl.
"<instance key>" --> Key name of the Oracle instance, minus the "key_" prefix (Registry key).
See this link ("Word character: \w" section) for a description of PowerShell RegEx word character ("\w") matching; basically letts/nums/punctuation (mainly underscore) ie chars Oracle allows/uses to form a legal listener name. You can also use the "." to consume any character.

Command to get name of signer from a .exe

I want to know if there is a command that will list the information that can be found in the Digital Signatures section of the properties of a .exe. Particularly I want to be able to grab the Name of the signer. Is there a command that will generate that information for me?
To get the subject name from the signer certificate used to create an Authenticode signature, you could use Get-AuthenticodeSignature:
PS > $asig = Get-AuthenticodeSignature 'C:\Windows\System32\xcopy.exe'
PS > $asig.SignerCertificate.Subject
CN=Microsoft Windows, O=Microsoft Corporation, L=Redmond, S=Washington, C=US
You're probably interested in the Common Name (CN), and maybe the Organization name (O). You can parse the Distinguished Name from Subject into its components to get the Common Name:
PS > $asig = Get-AuthenticodeSignature 'C:\Windows\System32\xcopy.exe'
PS > $dnDict = ($asig.SignerCertificate.Subject -split ', ') |
foreach `
{ $dnDict = #{} } `
{ $item = $_.Split('='); $dnDict[$item[0]] = $item[1] } `
{ $dnDict }
PS > $dnDict['CN']
Microsoft Windows
PS > $dnDict['O']
Microsoft Corporation
The Sigcheck tool from Microsoft's Windows Sysinternals can dump the information out. Using the -c command line option formats the output in a csv format which can be piped to a file for later processing.
Sigcheck is a command-line utility that shows file version number, timestamp information, and digital signature details, including certificate chains.
try signtool.exe. use following reference regarding signtool,
https://msdn.microsoft.com/en-us/library/windows/desktop/aa387764(v=vs.85).aspx
this is to output on the console:
writeln(GETDOSOutput('powershell write-host =(Get-AuthenticodeSignature ''C:\Program Files\Streaming\maxbox4\maXbox4.exe'').SignerCertificate.Subject', 'C:\'));

Resources