In Windows we access the Run box with Windows Key + R
. Then we can run a program by name (e.g. firefox, snippingtool). Is there a way to run a
As @briantist mentioned in his answer (+1): the "Run" dialog (which probably calls the ShellExecuteEx function) checks the subkeys of the App Paths
registry key in addition to the paths in the $env:PATH
environment variable.
For emulating the behavior of the "Run" dialog I would, however, add the folders to $env:PATH
instead of creating aliases for each executable:
$regkey = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths'
$appPaths = Get-ChildItem $regkey |
Get-ItemProperty |
? { $_.'(default)' } |
select -Expand '(default)' |
Split-Path -Parent |
% { [Environment]::ExpandEnvironmentVariables($_.TrimStart('"')) } |
select -Unique
$env:PATH += ';' + ($appPaths -join ';')
By adding this to a PowerShell profile (e.g. your personal profile %UserProfile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1
), the variable $env:PATH
of each PowerShell instance you start is updated with the additional paths from the registry. Note, however, that by adding too many folders you might be hitting length restrictions.
The run box uses the PATH
environment variable but it also uses a set of registry keys.
The key HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths
contains subkeys with certain application names, and each of those has a string value called Path
with the path to the executable. Powershell and the command prompt don't use these.
$allCommands = Get-Command -CommandType All | Select-Object -ExpandProperty Name
Get-ChildItem 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths' | Where-Object { $_.Property -icontains 'Path' } | ForEach-Object {
$executable = $_.Name | Split-Path -Leaf
$shortName = $executable -creplace '\.[^.]*$',''
$path = $_ | Get-ItemProperty | Select-Object -ExpandProperty Path
$fqPath = $path | Join-Path -ChildPath $executable
if ( ($allCommands -icontains $executable) -or ($allCommands -icontains $shortName) ) {
Write-Verbose "Skipping $executable and $shortName because a command already exists with that name."
} else {
Write-Verbose "Creating aliases for $executable."
New-Alias -Name $executable -Value $fqPath
New-Alias -Name $shortName -Value $fqPath
}
}
This snippet will read the registry and add all of those entries as aliases. It also adds the EXE name without .exe so that you can use the short name.
It checks for existing commands beforehand to make sure that it won't clobber any existing command (of any type).
I also created a function you could use to execute an arbitrary application, without modifying the entire environment:
function Run-Application {
[CmdletBinding(SupportsShouldProcess=$true)]
param(
[Parameter(
Mandatory=$true,
ValueFromPipeline=$true
)]
[ValidateNotNullOrEmpty()]
[String[]]
$Name ,
[Parameter()]
[Switch]
$NoExtension
)
Begin {
if (!$NoExtension -and $env:PATHEXT) {
$exts = $env:PATHEXT -csplit ';'
} else {
$exts = @()
}
$regStub = 'HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths'
}
Process {
:outer
foreach($app in $Name) {
$cmd2run = $app
if (Get-Command -Name $cmd2run -ErrorAction Ignore) {
if ($PSCmdlet.ShouldProcess($cmd2run)) {
& $cmd2run
}
continue :outer
} elseif ($app -cnotlike '*.*') {
foreach($ext in $exts) {
$cmd2run = "$app$ext"
if (Get-Command -Name $cmd2run -ErrorAction Ignore) {
if ($PSCmdlet.ShouldProcess($cmd2run)) {
& $cmd2run
}
continue :outer
}
}
}
$thisReg = $regStub | Join-Path -ChildPath $cmd2run
$regItem = $thisReg | Get-Item -ErrorAction Ignore
if ($regItem -and $regItem.Property -icontains 'Path') {
$thisPath = $regItem | Get-ItemProperty | Select-Object -ExpandProperty Path | Join-Path -ChildPath $cmd2run
if ($PSCmdlet.ShouldProcess($thisPath)) {
& $thisPath
}
continue :outer
} elseif ($app -cnotlike '*.*') {
foreach($ext in $exts) {
$cmd2run = "$app$ext"
$thisReg = $regStub | Join-Path -ChildPath $cmd2run
$regItem = $thisReg | Get-Item -ErrorAction Ignore
if ($regItem -and $regItem.Property -icontains 'Path') {
$thisPath = $regItem | Get-ItemProperty | Select-Object -ExpandProperty Path | Join-Path -ChildPath $cmd2run
if ($PSCmdlet.ShouldProcess($thisPath)) {
& $thisPath
}
continue :outer
}
}
}
}
}
}
You can supply one or more names, with or without extension, via parameter or via pipeline.
If the value has no extension, it uses PATHEXT
and tries every combination. You can also disable that by using -NoExtension
.
I used Run-Application
for verb-noun syntax, but you could always alias it to run
if you want.
I'm not sure if it will work for you if you had problems with the other code, but it worked very well for me, and I had fun writing it. Hope it's helpful to you or someone else.
Fixed function so it supports -WhatIf
.
Run-Application notepad
Run-Application firefox,chrome.exe
'notepad.exe','firefox.exe','snippingtool' | Run-Application -Whatif
Run-Application firefox -NoExtension # Should fail