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

这不是一个太牵强的想法:如果您正在与公司其他地方的其他命令行实用程序进行接口,它们为您提供了一个可以逐字运行的命令。因为不能控制命令,所以需要接受任何有效的命令作为输入。以下是我无法轻易克服的主要问题:

该命令可以执行位于路径中有空格的程序: $command = '"C:\Program Files\TheProg\Runit.exe" Hello'; 命令的参数中可以有空格: $command = 'echo "hello world!"'; 命令可以有单勾和双勾: $command = "echo ' "it " s "" ";

有什么干净的方法可以做到吗?我只能设计出奢侈而丑陋的变通方法,但对于一种脚本语言,我觉得这应该是非常简单的。


当前回答

调用表达式,也别名为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种情况之外,不应该有很多特殊情况。

其他回答

当我试图解析注册表中的卸载字符串并执行它们时,接受的答案对我不起作用。结果证明我根本不需要调用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是一个内部应用程序,我知道它只有两个参数。对于更复杂的卸载字符串,您可能需要使用连接操作符。另外,我只是使用了哈希映射,但实际上,你可能想使用数组。

此外,如果你确实安装了同一个应用程序的多个版本,这个卸载程序将一次循环遍历它们,这会混淆MsiExec.exe,所以也有这个问题。

调用表达式,也别名为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种情况之外,不应该有很多特殊情况。

请也看看微软的报告,本质上,使用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

如果你想使用调用操作符,参数可以是存储在变量中的数组:

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

调用操作符也使用ApplicationInfo对象。

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