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


208

我可以在以下情况下表达我的需要:编写一个函数,该函数接受要作为本机命令运行的字符串。

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

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

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

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

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

什么干净的方法可以做到这一点吗?我只能设计出繁琐和丑陋的解决方法,但是对于脚本语言,我觉得这应该变得非常简单。

Answers:


318

Invoke-Expression,也别名为iex。以下将对示例2和示例3起作用:

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种情况下应该没有很多特殊情况。


3
别名很棒。请记住,如果您移至另一台计算机或将该脚本发送给其他人,则可能不会设置别名。建议使用PowerShell函数的全名。
Doug Finke

1
@Doug:大多数时候,我会这样做或使用内置别名(尤其是在命令行中为简洁起见)。该eval事情是半开玩笑的,因为这是它被称为在如此众多的其他脚本语言,这是不是我见过那里有人还没有收到有关想法的第一个问题invoke-expression。OP的情况听起来仅是内部脚本。
Joel B Fant

我已经尝试过了,但是它不适用于:$ command ='“ C:\ Program Files \ Windows Media Player \ mplayer2.exe”“ H:\ Audio \ Music \ Stevie Wonder \ Stevie Wonder-Superstition.mp3” '
约翰尼·考夫曼

3
您可能需要输入“&”或“”。如果不是PowerShell的本地的,如实际的命令之前签署Invoke-Expression "& $command"
TorbjörnBergstedt

TorbjörnBergstedt获胜!神奇的额外“&”解决了我的问题!结果,我既高兴又慌张。
约翰尼·考夫曼

19

另请参阅此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

1
嗯,微软断开了互联网,链接不再有效。
约翰·布勒(JohanBoulé)

1

4

尝试解析注册表中的卸载字符串并执行它们时,可接受的答案对我不起作用。原来我根本不需要打给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
}

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

另外,如果您确实安装了同一应用程序的多个版本,则该卸载程序会一次循环浏览所有这些版本,这会造成混乱MsiExec.exe,因此也是如此。


1

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

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

调用运算符也可以使用ApplicationInfo对象。

$prog = get-command cmd
$myargs = -split '/c dir /x'
& $prog $myargs
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.