如何从字符串执行任意本机命令?

How do you execute an arbitrary native command from a string?

提问人:Johnny Kauffman 提问时间:6/14/2011 最后编辑:SteveCJohnny Kauffman 更新时间:11/21/2019 访问量:309308

问:

我可以通过以下场景来表达我的需求:编写一个函数,该函数接受要作为本机命令运行的字符串。

这并不是一个太牵强的想法:如果你正在与公司其他地方的其他命令行实用程序进行交互,这些实用程序会为你提供逐字运行的命令。由于您不控制该命令,因此需要接受任何有效的命令作为输入。这些是我无法轻松克服的主要问题:

  1. 该命令可能会执行位于带有空格的路径中的程序:

    $command = '"C:\Program Files\TheProg\Runit.exe" Hello';
    
  2. 该命令可能包含带空格的参数:

    $command = 'echo "hello world!"';
    
  3. 该命令可能同时具有单刻度和双刻度:

    $command = "echo `"it`'s`"";
    

没有干净的方法可以做到这一点?我只能设计出奢华而丑陋的解决方法,但对于脚本语言,我觉得这应该非常简单。

编写 PowerShell

评论


答:

365赞 Joel B Fant 6/14/2011 #1

Invoke-Expression,也别名为 。以下内容将适用于您的示例 #2 和 #3:iex

iex $command

某些字符串不会按原样运行,例如示例 #1,因为 exe 用引号括起来。这将按原样工作,因为字符串的内容正是直接从 Powershell 命令提示符运行它的方式:

$command = 'C:\somepath\someexe.exe somearg'
iex $command

但是,如果 exe 用引号括起来,则需要帮助才能使其运行,如本例所示,从命令行运行:&

>> &"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"

然后在脚本中:

$command = '"C:\Program Files\Some Product\SomeExe.exe" "C:\some other path\file.ext"'
iex "& $command"

很可能,您可以通过检测命令字符串的第一个字符是否为 来处理几乎所有情况,就像在这个朴素的实现中一样:"

function myeval($command) {
    if ($command[0] -eq '"') { iex "& $command" }
    else { iex $command }
}

但是,您可能会发现其他一些情况必须以不同的方式调用。在这种情况下,您将需要使用 ,(可能用于特定的异常类型/消息)或检查命令字符串。try{}catch{}

如果您总是收到绝对路径而不是相对路径,则除了上述 2 种情况外,您不应该有很多特殊情况(如果有的话)。

评论

5赞 Doug Finke 6/14/2011
混叠很棒。请记住,如果您移动到另一台计算机或将该脚本发送给其他人,则可能不会设置该别名。首选 PowerShell 函数的全名。
1赞 Joel B Fant 6/14/2011
@Doug:大多数时候我这样做或使用内置别名(特别是为了命令行的简洁)。这些事情是半开玩笑的,因为这就是许多其他脚本语言中的名称,这不是我看到的第一个有人不知道的问题。OP 的案例听起来只是内部脚本。evalinvoke-expression
0赞 Johnny Kauffman 6/14/2011
我试过了,但它不适用于:$command = '“C:\Program Files\Windows Media Player\mplayer2.exe” “H:\Audio\Music\Stevie Wonder\Stevie Wonder - 迷信.mp3”'
5赞 Torbjörn Bergstedt 6/14/2011
如果实际命令不是 powershell 原生的,则可能需要在实际命令之前放置“&”或“.”符号,例如Invoke-Expression "& $command"
1赞 Stephen Lee Parker 2/7/2019
我无法以我需要的方式让上述内容为我工作。我最终使用了&cmd /c $command它不需要修改引号$command
4赞 yurisich 9/11/2012 #2

当尝试解析注册表中的卸载字符串并执行它们时,接受的答案对我不起作用。事实证明,我根本不需要这个电话。Invoke-Expression

我终于遇到了这个不错的模板,用于了解如何执行卸载字符串:

$path = 'HKLM:\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall'
$app = 'MyApp'
$apps= @{}
Get-ChildItem $path | 
    Where-Object -FilterScript {$_.getvalue('DisplayName') -like $app} | 
    ForEach-Object -process {$apps.Set_Item(
        $_.getvalue('UninstallString'),
        $_.getvalue('DisplayName'))
    }

foreach ($uninstall_string in $apps.GetEnumerator()) {
    $uninstall_app, $uninstall_arg = $uninstall_string.name.split(' ')
    & $uninstall_app $uninstall_arg
}

这对我有用,因为这是一个内部应用程序,我知道它只有两个参数。对于更复杂的卸载字符串,您可能需要使用 join 运算符。另外,我只是使用了哈希映射,但实际上,您可能想要使用数组。$app

此外,如果您确实安装了同一应用程序的多个版本,则此卸载程序将一次循环浏览它们,这会造成混淆,因此也存在。MsiExec.exe

评论

0赞 NiKiZe 6/3/2022
这假定卸载路径没有任何空格。
23赞 Luke Puplett 2/19/2014 #3

另请参阅此 Microsoft Connect 报告,从本质上讲,使用 PowerShell 运行 shell 命令是多么困难(哦,具有讽刺意味的是)。

http://connect.microsoft.com/PowerShell/feedback/details/376207/

他们建议使用 作为一种强制 PowerShell 停止尝试向右解释文本的方法。--%

例如:

MSBuild /t:Publish --% /p:TargetDatabaseName="MyDatabase";TargetConnectionString="Data Source=.\;Integrated Security=True" /p:SqlPublishProfilePath="Deploy.publish.xml" Database.sqlproj

评论

2赞 Johan Boulé 5/24/2018
啊,Microsoft破坏了互联网,链接不再有效。
4赞 mwfearnley 8/28/2018
以上链接 archive.org 网址为 web.archive.org/web/20131122050220/http://...
2赞 Johan Boulé 11/11/2021
我喜欢这篇文章。完全总结了“pwsh”不是外壳。
6赞 js2010 11/21/2019 #4

如果要使用调用运算符,参数可以是存储在变量中的数组:

$prog = 'c:\windows\system32\cmd.exe'
$myargs = '/c','dir','/x'
& $prog $myargs

调用运算符也适用于 ApplicationInfo 对象。

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs

评论

1赞 js2010 11/29/2021
@YoraiLevi参数模式与表达模式& $wsl (-split "--install -d Ubuntu-20.04")