Java MSI 静默安装程序

Java MSI silent installer

你好,我一直在处理的这行代码有问题,由于某种原因它不再工作了我正在尝试静默调用 MSI 安装程序并等待它完成,这样我就可以执行下一行代码我让它工作但现在不工作,我尝试执行启动过程并使用 -wait 参数但它返回错误消息,当我调用“msiexec.exe”时它没有 -wait 参数

msiexec.exe /i "C:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.log"
Write-Host -NoNewLine "Done.`n" -ForegroundColor Cyan
Start-Sleep -s 2



  start-process msiexec.exe  "/i C:\MagtekCC\Files\Java\jre1.8.0_144.msi" -wait /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.log" 
     Write-Host -NoNewLine "Done.`n" -ForegroundColor Cyan
      Start-Sleep -s 2

> Start-Process : A positional parameter cannot be found that accepts argument '/QN'.
At line:1 char:1

    + start-process msiexec.exe  "/i C:\MagtekCC\Files\Java\jre1.8.0_144.ms ...
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo          : InvalidArgument: (:) [Start-Process], ParameterBindingException
        + FullyQualifiedErrorId : PositionalParameterNotFound,Microsoft.PowerShell.Commands.StartProcessCommand

msiexec.exe 是一个 GUI 工具,Windows CLI(命令提示符或 PowerShell)默认不会阻止。相反,控制台应用程序将默认阻止。在 PowerShell 中,您可以使用 Start-Process.

更改此默认行为(以及获得有关子进程的额外控制和信息)

Note: This answer focuses on Start-Process but the equivalent way to force a program to wait before continuing in the Command Prompt or .bat script is

start "title" /WAIT [command/program] [parameters]

Note that the "title" can be a blank string "".


由于您已使用 Start-Process 尝试更新了问题,因此它失败了,因为您在 -ArgumentList 参数的中间提供了 -Wait 参数。 -ArgumentList 是专门定义的,所以它后面所有剩余的位置参数都被认为是它的一部分。问题是,当您在此处粘贴另一个命名参数时,您现在已经破坏了参数链。解决方案如下所示,但请确保您提供的 -ArgumentList 参数没有与其他参数分开 Start-Process:

$msiExecArgs = @(
  '/i',
  'C:\MagtekCC\Files\Java\jre1.8.0_144.msi',
  '/QN',
  '/L*V', 'C:\Temp\msilog.log',
  'TRANSFORMS="jre1.8.0_144.mst'
) -join ' '

Start-Process msiexec -Wait -ArgumentList $msiExecArgs

其工作方式如下:

  • 创建一个包含 msiexec 个参数的数组。这可以是每个 space 分隔的参数的数组,您通常会以与交互指定相同的顺序传递给 msiexec.exe。或者,您也可以将其作为单个字符串提供。阅读下面的子点以了解为什么我将其声明为数组,但 -join 将其声明为字符串。
      建议
    • Due to this bug 以字符串形式提供参数,这就是为什么我采用数组参数并使用 -join 从中创建一个 space 分隔的字符串。数组代码更容易管理,因为现在我们不必使用 +=(严重且不推荐)或 StringBuilder(推荐超过 +=,但对于构​​建数组来说太过分了参数),我们仍然得到避免错误的字符串。
  • -Wait 告诉 Start-Process 阻止进一步执行,直到程序退出。
  • -ArgumentList 用于将我们的 $msiExecArgs 个参数数组传递给 msiexec.

Note: A prior revision of this answer incorrectly stated that Start-Process needs
-Wait:$false in order not to block on execution for CLI applications. While the default behavior for external programs is to block for CLI and not block for GUI, Start-Process actually ignores this and does not block by default regardless of the program type. Use the -Wait parameter to block, and omit or provide -Wait:$false not to block.

相反,如果您想 运行 在后台执行 CLI 程序,请执行与上述相同的操作,但省略 -Wait 参数(您可能还想检查结果这需要一些额外的肘部润滑脂 -PassThru 所以我们可以检查它是否完成及其退出代码):

$p = Start-Process ping -PassThru -ArgumentList www.google.com

# Do other stuff, then check the result when ready
Write-Host "Checking that ping.exe has finished"
while( !$p.HasExited ) {

  # Wait 1 second before checking again
  Start-Sleep 1
}

if( $p.ExitCode -ne 0 ) {
  Write-Error "ping failed with exit code $($p.ExitCode)"
}
  • ping.exe为例,我们运行通过Start-Processping.exe 是一个控制台应用程序,通常会阻塞直到执行完成。
  • -PassThru是用来告诉Start-Process到return的Process对象。 Start-Process 默认情况下不会 return 任何东西,但在这种情况下,我们稍后要检查是否已完成 运行ning 以及是否成功。这是可选的,但需要稍后检查结果。
  • while 循环等待 $p.HasExited 到 return 为真。 Process.HasExited用于判断流程执行是否结束,无论成功与否。
  • if 语句检查 Process 对象的退出代码 returned 0。作为一个标准问题,如果一个程序 returns 0 通常可以被认为是成功的,尽管如果程序使用其他代码来表示不同的结束状态,您可以指定一个可接受的退出代码数组。

您还可以对进程对象执行其他操作,请尝试一下。然而,虽然 Start-Process 可以是一个强大的工具,但它经常被过度使用,不必要地使不需要它的代码复杂化。虽然您的 MSIEXEC 用例是何时使用它的完美示例,但通常您可以通过完全省略 Start-Process 并依赖调用运算符 & 来执行程序来简化 Start-Process 的使用。

msiexec.exe 的不寻常之处在于,虽然它具有扩展的 CLI(命令行界面)和许多支持的参数,但它是一个 GUI-子系统可执行文件独立于任何调用控制台运行。

当您直接调用 GUI 子系统可执行文件时,PowerShell 异步运行;也就是说,与 console 子系统可执行文件不同,它 不会等待可执行文件终止 才继续。

虽然 Start-Process -Wait 确实是 同步调用 GUI 子系统可执行文件 的典型方式,还有一个方便的快捷方式:

如果你管道到Write-Output[1]

  • PowerShell 等待可执行文件(的进程)终止,即使它是一个 GUI -子系统应用程序。

    • 注意:如果您知道 GUI 应用程序 没有标准输出输出 - 并且 没有 这样做是典型的- 您还可以通过管道传输到 Wait-Process 以获得更好的概念清晰度(再次参见脚注 [1])。
  • 此外,automatic $LASTEXITCODE variable 将正确反映进程的退出代码GUI-子系统可执行文件有意义地设置,但 misexec.exe 确实如此),允许您推断 成功 (退出代码 0)与 失败 (任何 非零 退出代码),按照惯例。

因此,您可以使用以下方法:

# Note the `| Write-Output` at the end of the line, which ensures *synchronous* execution.
msiexec.exe /i "C:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.log" | Write-Output
$exitCode = $LASTEXITCODE

Write-Host -ForegroundColor Cyan "Installation finished with exit code $exitCode."

上面是下面Start-Process -Wait解决方案的更方便的替代方案,它也使用-PassThru传递一个表示已启动进程的对象,使其.ExitCode属性 可以检查:

$exitCode = (Start-Process -Wait -NoNewWindow -PassThru msiexec.exe @'
/i "C:\MagtekCC\Files\Java\jre1.8.0_144.msi" /QN TRANSFORMS="jre1.8.0_144.mst" /L*V "C:\Temp\msilog.log"
'@).ExitCode

Write-Host -ForegroundColor Cyan "Installation finished with exit code $exitCode."

注意:-NoNewWindow 通常在这种情况下没有区别,但会允许来自那些显式附加到调用者控制台的罕见 GUI 应用程序的控制台输出(参见脚注 [1])当前控制台中的表面 - 尽管无法 捕获 它。

请注意 misexex.exe 所有 个参数是如何 作为单个字符串 传递给 Start-Process(此处使用 here-string 以提高可读性),它在位置上绑定到 -ArgumentList 参数。

虽然 -ArgumentList 参数在技术上接受一个 array 参数 - 即允许您 单独 传递参数 - 很长- 存在的错误使得这样做不可取 - 请参阅


至于你试过的

您的 Start-Process -Wait 调用失败,因为您最终传递了 两个以上的 位置参数:

start-process msiexec.exe  "/i ..." -wait /QN ...
  • msiexec.exe 位置绑定到 -FilePath 参数。
  • 只有 "/i ..." 参数位置参数绑定到 -ArgumentList 参数。
  • 因为没有其他 Start-Process 参数支持位置绑定,Start-Process 不知道如何处理 /QN(以及所有后续的位置(未命名)参数),这就是您问题中的错误消息表明。

注意:如果您确实想 单独传递参数 - 最好避免,如上所述 - 您必须将它们作为 数组传递,以,
分隔 (Start-Process -Wait msiexec.exe '/i', ...)


[1] 请注意,后续管道段中的 any 命令可以确保同步执行,因为 PowerShell 始终必须等待前面段的命令终止,以确保已收到所有输入;虽然最终你选择什么命令并不重要,Write-Output,在这种情况下 通常 是一个空操作,具有传递 stdout 输出的优点,以便它变得可见/可以被捕获,如果手头的GUI应用程序产生这样的输出,这是罕见的(附加2>&1到GUI应用程序调用也捕获stderr 输出)。如Bender the Greatest points out, some GUI applications do so in order to produce debugging output, by explicitly attaching to the caller's console.