【问题标题】:Run a program by name from PowerShell (similarly to the run box)从 PowerShell 按名称运行程序(类似于运行框)
【发布时间】:2014-10-17 07:39:34
【问题描述】:

在 Windows 中,我们使用Windows Key + R 访问运行框。然后我们可以按名称运行程序(例如 firefox、snippingtool)。有没有办法从 PowerShell 中按名称运行程序?

> run firefox
> run snippingtool

我已经尝试过Start-Process firefox,但还是不行。

编辑

奇怪的是,在 PowerShell 和“运行”框中,只需键入 notepad 即可打开记事本。但是,键入 firefoxsnippingtool 只能在“运行”框中使用,而不能在 PowerShell 中使用。

【问题讨论】:

  • 也许我误解了你的问题,但你应该可以在命令行中输入应用程序的名称并回车。只要应用程序名称在路径上,它就应该启动。例如,我可以打开一个 powershell 终端并输入 mspaint.exe,然后按 Enter 并启动 Paint。
  • @David 我也是这么想的。事实证明,对于某些程序,例如 notepad,我可以在 PowerShell 中输入应用程序的名称并按 Enter,而对于其他程序,例如 firefoxsnippingtool,运行框只是从它们的应用程序名称,但 PowerShell 没有。
  • 您可以只输入应用程序的名称(例如“记事本”),因为它位于 Windows 的 system32 目录中。您可以将任何程序放在那里并按名称调用它,但这是一种糟糕的做法。您是否有理由不想使用您正在启动的程序的路径?我个人通常在启动程序时使用带有 Start-Process 的完整路径。
  • 正如@David 所说:可执行文件的路径需要在您的%PATH%/$env:PATH 环境变量中。 “运行”对话框的行为不同,因为它使用的搜索路径与 CMD 和 PowerShell 不同。
  • @ShaunLuttin 如果您不介意每次都输入它,打开 powershell,输入“notepad $profile”,现在创建一个包含程序路径的新变量,例如$programIwantToRun = "C:\program.exe"。现在,每次打开 PowerShell 时都会设置该变量,您只需引用该变量即可。您应该注意,在提升的进程(以管理员身份运行)下运行的 PowerShell 会话获得的 $Profile 与作为非提升进程运行的会话不同。

标签: powershell


【解决方案1】:

正如@briantist 在他的回答 (+1) 中提到的:“运行”对话框(probably calls the ShellExecuteEx function)除了检查$env:PATH 环境变量中的路径之外,还会检查App Paths 注册表项的子项。

但是,为了模拟“运行”对话框的行为,我会将文件夹添加到 $env:PATH,而不是为每个可执行文件创建别名:

$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 ';')

通过将其添加到 PowerShell profile(例如您的个人资料 %UserProfile%\Documents\WindowsPowerShell\Microsoft.PowerShell_profile.ps1),您启动的每个 PowerShell 实例的变量 $env:PATH 都会使用注册表中的其他路径进行更新。但是请注意,如果添加太多文件夹,您可能会点击length restrictions

【讨论】:

  • 在我的测试中,在运行时更改PATH变量并没有影响运行环境,并且无法执行添加到路径中的新目录中的文件。
  • @briantist 该代码仅更改进程的环境变量副本,在运行时运行良好。不过,这种变化不是持久的,这就是为什么我建议将它添加到 PowerShell 配置文件中。
  • 环境变量为我改变了(当然只在会话期间),但尝试在(现在改变的)路径中运行可执行文件没有。这对你有用吗?
  • @briantist 是的,使用配置文件和动态更新变量。
  • 我刚刚又试了一次,你是对的。我确实看到了预期的行为。不知道我第一次做错了什么,尽管这就是我最终改用New-Alias 的原因。
【解决方案2】:

运行框使用PATH 环境变量,但它也使用一组注册表项。

HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths 键包含具有特定应用程序名称的子键,每个子键都有一个名为 Path 的字符串值,其中包含可执行文件的路径。 Powershell 和命令提示符不使用这些。

在 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
    }
}

这个 sn-p 将读取注册表并将所有这些条目添加为别名。它还添加了不带 .exe 的 EXE 名称,以便您可以使用短名称。

它预先检查现有命令以确保它不会破坏任何现有命令(任何类型)。

编辑

我还创建了一个函数,您可以使用它来执行任意应用程序,而无需修改整个环境:

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
                    }                    
                }
            }
        }
    }
}

您可以通过参数或管道提供一个或多个名称,带或不带扩展名。

如果该值没有扩展名,它将使用PATHEXT 并尝试所有组合。您也可以使用 -NoExtension 禁用它。

我使用Run-Application 表示动词-名词语法,但如果您愿意,您始终可以将其别名为run

如果您在使用其他代码时遇到问题,我不确定它是否适合您,但它对我来说效果很好,而且我写得很开心。希望对您或其他人有所帮助。

编辑 2

固定功能,支持-WhatIf

示例

Run-Application notepad

Run-Application firefox,chrome.exe

'notepad.exe','firefox.exe','snippingtool' | Run-Application -Whatif

Run-Application firefox -NoExtension # Should fail

【讨论】:

  • 运行该代码时,我收到以下错误:Join-Path : Cannot bind argument to parameter 'Path' because it is an empty string. At 32 char:23 $fqPath = $path | Join-Path -ChildPath $executable. 实际上,运行该代码时出现了六个错误。
  • 当它抛出错误时,你有没有机会单步执行并给我变量的值?到目前为止,我无法重现自己的结果,但我敢打赌我没有考虑到某些情况。
  • 我必须用“SilentlyContinue”替换“Ignore”才能正常工作。 Ignore 在 PowerShell 2.0 中不是有效的 ErrorAction
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-01-29
  • 1970-01-01
  • 1970-01-01
  • 2014-07-27
  • 2015-04-10
  • 2011-11-18
  • 1970-01-01
相关资源
最近更新 更多