Run a program by name from PowerShell (similarly to the run box)

前端 未结 2 1532
你的背包
你的背包 2021-01-07 09:45

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

2条回答
  •  别那么骄傲
    2021-01-07 10:29

    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.

    Emulating this in Powershell

    $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).

    Edit

    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.

    Edit 2

    Fixed function so it supports -WhatIf.

    Examples

    Run-Application notepad
    
    Run-Application firefox,chrome.exe
    
    'notepad.exe','firefox.exe','snippingtool' | Run-Application -Whatif
    
    Run-Application firefox -NoExtension # Should fail
    

提交回复
热议问题