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

时间:2011-06-14 00:54:59

标签: scripting powershell

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

这个想法并不是太遥远:如果你正在与公司其他地方的其他命令行实用程序连接,那么它将为你提供逐字运行的命令。因为您不控制命令,所以您需要接受任何有效的命令作为输入。这些是我无法轻易克服的主要障碍:

  1. 该命令可能会执行生活在路径中的程序,其中包含空格:

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

    $command = 'echo "hello world!"';
    
  3. 该命令可能包含单蜱和双蜱:

    $command = "echo `"it`'s`"";
    
  4. 任何干净的方式来实现这个目标吗?我只能设计出丰富而丑陋的变通方法,但对于一种脚本语言,我觉得这应该很简单。

4 个答案:

答案 0 :(得分:276)

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之外有许多特殊情况(如果有的话)。

答案 1 :(得分:16)

请基本上看看这个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 :(得分:4)

在尝试解析注册表中的卸载字符串并执行它们时,接受的答案对我不起作用。事实证明,毕竟我并不需要拨打Invoke-Expression

我终于遇到了这个nice template,看看如何执行卸载字符串:

$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 operator。另外,我刚刚使用了哈希映射,但实际上,您可能想要使用数组。

此外,如果您确实安装了同一个应用程序的多个版本,则此卸载程序将同时循环显示所有这些版本,这会使MsiExec.exe混淆,因此也会出现这种情况。

答案 3 :(得分:0)

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

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

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

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