【问题标题】:Java MSI silent installerJava MSI 静默安装程序
【发布时间】:2021-11-04 16:19:17
【问题描述】:

您好,我一直在处理的这行代码有问题,由于某种原因不再工作我正在尝试静默调用 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

【问题讨论】:

  • 再次感谢您的帮助刚刚标记了您提供的答案我试图找到接受它的按钮但您帮我找到了它
  • 很高兴听到这个消息;我的荣幸。
  • @Eslendil,你为什么把这个发布为your own question的副本,然后是plagiarize@mklement0's answertwice?
  • 这能回答你的问题吗? Java Silent installer command
  • 对不起,我对这个网站的功能还很熟悉,如果我做错了什么,我深表歉意

标签: java powershell windows-installer synchronous


【解决方案1】:

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

当您直接调用它们时,PowerShell异步运行 GUI 子系统可执行文件;也就是说,与 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 在这种情况下通常没有区别,但会允许来自那些显式附加到调用者控制台(见脚注 [1])的罕见 GUI 应用程序的控制台输出显示在当前控制台中 -尽管没有能力捕获它。

注意misexex.exe 的所有 参数如何作为单个字符串 传递给Start-Process(为了便于阅读,使用here-string这里),它在位置上绑定到-ArgumentList 参数。

虽然-ArgumentList 参数在技术上接受参数的数组 - 即允许您单独 传递参数 - 一个长期存在的错误使得这是不可取的 - 请参阅@987654325 @。


至于你尝试了什么:

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

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

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


[1] 请注意,后续管道段中的 any 命令可确保同步执行,因为 PowerShell 始终必须等待前一段的命令终止,以确保已收到所有输入;虽然最终选择什么命令并不重要,但Write-Output,在这种情况下通常是一个无操作,具有传递标准输出输出的优点,以便它变得可见/可以被捕获,if 手头的 GUI 应用程序会产生这样的输出,这很少见(将 2>&1 附加到 GUI 应用程序调用以捕获 stderr 输出)。正如Bender the Greatest 指出的那样,一些GUI 应用程序这样做是为了产生调试输出,明确地attaching to the caller's console。

【讨论】:

  • 巧妙地将管道连接到Out-Null 以阻止执行。这种行为是否仅适用于Out-Null,或者这是否延伸到一般的管道?例如。到Write-Output 或Write-Verbose 的管道会起作用吗?
  • 这不一定是真的。可以使 GUI 应用程序写入 STDOUT 和 STDERR,如果在控制台中运行,则会将输出写入控制台。我曾经在做这个的软件上工作。尽管我承认我可能记错了,但该项目可能已构建为控制台应用程序,然后调用 GUI 组件;这将支持您所说的,因为它在技术上仍将构建为控制台应用程序。
  • 但是,is it still possible 使用可执行构建的 Windows 应用程序写入控制台
  • 我在两种情况下这样做了:已经定义为 GUI 应用程序的程序,然后需要稍后提供控制台输出。在某些情况下,更改程序类型可能被视为重大更改,部分原因是执行行为不同。第二种情况是为已经定义为 GUI 应用程序的程序提供调试或跟踪级别的输出。通常我会同意并且不推荐这种方法,但我当时正在开发的产品套件在某种程度上是定制的功能,并且将程序更改为控制台会为主要客户破坏一些东西。
  • @BendertheGreatest,是的,这些参数只支持捕获流输出in files(你不能merge流,除非你调用通过 shell 并使 2>&1 成为 shell 命令的一部分)。能够在变量中捕获确实很好,因为可以直接执行(但不能与2>分开);在任何预期文件路径的地方允许命名空间表示法(variable:output)对于一个伟大的未来增强,在GitHub issue #5184和#4332中讨论
【解决方案2】:

msiexec.exe 是一个 GUI 工具,默认情况下 Windows CLI(命令提示符或 PowerShell)不会阻止。相反,控制台应用程序将默认阻止。在 PowerShell 中,您可以使用 Start-Process 更改此默认行为(以及获得有关子进程的额外控制和信息)。

注意:此答案侧重于Start-Process,但在命令提示符或.bat 脚本中继续之前强制程序等待的等效方法是

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

注意“title”可以是空字符串""。


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

注意:此答案的先前版本错误地指出 Start-Process 需要
-Wait:$false 以便不阻止 CLI 应用程序的执行。虽然外部程序的默认行为是阻止 CLI 而不是阻止 GUI,但Start-Process 实际上忽略了这一点,并且无论程序类型如何,默认情况下都不会阻止。使用-Wait参数进行屏蔽,省略或提供-Wait:$false不屏蔽。

相反,如果您想在后台运行 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-Process 运行它。 ping.exe 是一个控制台应用程序,通常会阻塞直到执行完成。
  • -PassThru 用于告诉Start-Process 返回Process 对象。 Start-Process 默认情况下不返回任何内容,但在这种情况下,我们稍后要检查它是否已完成运行以及它是否成功。这是可选的,但需要稍后检查结果。
  • while 循环等待$p.HasExited 返回真。 Process.HasExited用于判断流程执行是否完成,无论成功与否。
  • if 语句检查Process 对象的退出代码是否返回0。按照标准,如果程序返回 0,则通常可以认为它是成功的,但如果程序使用其他代码用于不同的结束状态,您可以指定一个可接受的退出代码数组。

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

【讨论】:

  • 感谢您的冗长回复以及提供的信息非常有帮助我非常感谢,我继续测试它,它似乎按预期工作,但我很想知道为什么我使用启动进程 msiexec.exe 没有。我已经在主帖上传了错误代码。
  • 两个原因:您在 msiexec 参数之间卡住了 -Wait,它应该是 Start-Process 的直接参数,而且您没有将 -ArgumentList 参数作为字符串数组提供。查看我设置 $msiExecArgs 的代码块及其后面的解释,并将其与您的代码进行比较。如果你还是不明白,我会在不使用手机时尝试进一步解释。
猜你喜欢
  • 2013-03-01
  • 2011-06-04
  • 1970-01-01
  • 2023-04-06
  • 1970-01-01
  • 1970-01-01
  • 2012-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多