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-Process
。 ping.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.
你好,我一直在处理的这行代码有问题,由于某种原因它不再工作了我正在尝试静默调用 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 isstart "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
(推荐超过+=
,但对于构建数组来说太过分了参数),我们仍然得到避免错误的字符串。
- Due to this bug 以字符串形式提供参数,这就是为什么我采用数组参数并使用
-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-Process
。ping.exe
是一个控制台应用程序,通常会阻塞直到执行完成。 -PassThru
是用来告诉Start-Process
到return的Process
对象。Start-Process
默认情况下不会 return 任何东西,但在这种情况下,我们稍后要检查是否已完成 运行ning 以及是否成功。这是可选的,但需要稍后检查结果。while
循环等待$p.HasExited
到 return 为真。Process.HasExited
用于判断流程执行是否结束,无论成功与否。if
语句检查Process
对象的退出代码 returned0
。作为一个标准问题,如果一个程序 returns0
通常可以被认为是成功的,尽管如果程序使用其他代码来表示不同的结束状态,您可以指定一个可接受的退出代码数组。
您还可以对进程对象执行其他操作,请尝试一下。然而,虽然 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])。
- 注意:如果您知道 GUI 应用程序 没有标准输出输出 - 并且 没有 这样做是典型的- 您还可以通过管道传输到
此外,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.