创建for循环以整合powershell菜单驱动脚本
我一直在编写一个脚本来安装一个需要几个安装程序才能正常工作的产品。有些需要安装某些功能,而有些是可选的。我的脚本是菜单驱动的,它支持可选的安装程序,并且工作得相当好 大部分代码都被重用了,我想将其整合到一个for循环中,但我的pshellfoo还没有达到黑带级别。我熟悉多变量for循环的CSV输入,但我认为这可以通过代码中生成的数组来实现。你们中的一位大师能训练这个新手吗 以下是当前代码的净化版本:创建for循环以整合powershell菜单驱动脚本,powershell,Powershell,我一直在编写一个脚本来安装一个需要几个安装程序才能正常工作的产品。有些需要安装某些功能,而有些是可选的。我的脚本是菜单驱动的,它支持可选的安装程序,并且工作得相当好 大部分代码都被重用了,我想将其整合到一个for循环中,但我的pshellfoo还没有达到黑带级别。我熟悉多变量for循环的CSV输入,但我认为这可以通过代码中生成的数组来实现。你们中的一位大师能训练这个新手吗 以下是当前代码的净化版本: # Main Level Menu Choices switch (Read-Host $Mai
# Main Level Menu Choices
switch (Read-Host $Main_Prompt)
{
1 {$choice = 1}
2 {$choice = 2}
3 {$choice = 3}
4 {$choice = 4}
5 {$choice = 5}
"q" {$choice = q}
}
if (($choice -eq "q"){
Write-host "Exiting!"
exit
}
# Install 1
if (($choice -eq 1) -or ($choice -eq 5)){
Write-Host "Installing 1"
$1Path = "$InstallSource\Folder1\file.msi"
if (Test-Path $1Path) {
$processInfo = New-Object System.Diagnostics.ProcessStartInfo "msiexec.exe"
$processInfo.arguments = "/i `"$1Path`" /qr ARGUMENTS"
$processMSI = New-Object System.Diagnostics.Process
$processMSI.StartInfo = $processInfo
$processMSI.Start() | Out-Null
$processMSI.WaitforExit()
if ($processMSI.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processMSI.ExitCode "Exiting!"
exit
}
}
Else {
Write-host "Installer does not exist. Exiting!"
exit
}
}
# Install 2
if (($choice -eq 2) -or ($choice -eq 5)){
Write-Host "Installing 2"
$2Path = "$InstallSource\Folder2\file.msi"
$processInfo = New-Object System.Diagnostics.ProcessStartInfo "msiexec.exe"
$processInfo.arguments = "/i `"$2Path`" /qr ARGUMENTS"
$processMSI = New-Object System.Diagnostics.Process
$processMSI.StartInfo = $processInfo
# Write-host $ProcessInfo.arguments
$processMSI.Start() | Out-Null
$processMSI.WaitforExit()
if ($processMSI.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processMSI.ExitCode "Exiting!"
exit
}
}
# Install 3
if (($choice -eq 3) -or ($choice -eq 5)){
Write-Host "Installing 3"
$3Path = "$InstallSource\Folder3\file.exe"
$processInfo = New-Object System.Diagnostics.ProcessStartInfo $3Path
$processInfo.arguments = "/S /v`" ARGUMENTS`""
$processEXE = New-Object System.Diagnostics.Process
$processEXE.StartInfo = $processInfo
$processEXE.Start() | Out-Null
$processEXE.WaitforExit()
if ($processEXE.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!"
exit
}
}
# Install 4
if (($choice -eq 4) -or ($choice -eq 5)){
Write-Host "Checking for the Desktop-Experience Feature"
$DEStatus = Get-WindowsFeature Desktop-Experience
if( $DEStatus.Installed -ne "True" ) {
Write-Host "Installing Desktop-Experience Feature"
Import-Module ServerManager
Add-WindowsFeature -Name Desktop-Experience
}
Write-Host "Installing 4"
$4Path = "$InstallSource\Folder4\file.exe"
$processInfo = New-Object System.Diagnostics.ProcessStartInfo $4Path
$processInfo.arguments = "ARGUMENTS"
$processEXE = New-Object System.Diagnostics.Process
$processEXE.StartInfo = $processInfo
$processEXE.Start() | Out-Null
$processEXE.WaitforExit()
if ($processEXE.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!"
exit
}
# Install 5
Write-Host "Installing 5"
$5Path = "$InstallSource\Folder5\file.exe"
$processInfo = New-Object System.Diagnostics.ProcessStartInfo $5Path
$processInfo.arguments = "ARGUMENTS"
$processEXE = New-Object System.Diagnostics.Process
$processEXE.StartInfo = $processInfo
$processEXE.Start() | Out-Null
$processEXE.WaitforExit()
if ($processEXE.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!"
exit
}
}
# Install 6
if (($choice -eq 3) -or ($choice -eq 5)){
Write-Host "Installing 6"
$6Path = "$InstallSource\Folder6\file.exe"
$processInfo = New-Object System.Diagnostics.ProcessStartInfo $6Path
$processInfo.arguments = "ARGUMENTS"
$processEXE = New-Object System.Diagnostics.Process
$processEXE.StartInfo = $processInfo
$processEXE.Start() | Out-Null
$processEXE.WaitforExit()
if ($processEXE.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!"
exit
}
}
# Install 7
if (($choice -eq 4) -or ($choice -eq 5)){
Write-Host "Installing 7"
$7Path = "$InstallSource\Folder7\file.exe"
$processInfo = New-Object System.Diagnostics.ProcessStartInfo $7Path
$processInfo.arguments = "ARGUMENTS"
$processEXE = New-Object System.Diagnostics.Process
$processEXE.StartInfo = $processInfo
$processEXE.Start() | Out-Null
$processEXE.WaitforExit()
if ($processEXE.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processEXE.ExitCode "Exiting!"
exit
}
}
编辑:我使用了MadTechnician的代码,对其进行了简化并运行,以查看发生了什么。以下是测试代码:
$App1 = "Write-host App1"
$App2 = "Write-host App2"
$App3 = "Write-host App3"
$App4 = "Write-host App4"
$App5 = "Write-host App5"
$App6 = "Write-host App6"
$App7 = "Write-host App7"
# Main Level Menu Choices
$Main_Prompt = '
[1] Choice1
[2] Choice2
[3] Choice3
[4] Choice4
[5] Choice5
[q] Quit
'
switch (Read-Host $Main_Prompt)
{
{$_ -eq 1 -or $_ -eq 5} {$ToInstall += $App1}
{$_ -eq 2 -or $_ -eq 5} {$ToInstall += $App2}
{$_ -eq 3 -or $_ -eq 5} {$App3,$App4|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}}
{$_ -eq 4 -or $_ -eq 5} {$App5,$App6,$App7|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}}
{$_ -ieq "q"} {Write-host "Exiting!";Exit}
}
ForEach($App in ($ToInstall | Select -Unique)){
Invoke-Expression $App
}
以下是结果(在代码块中,因为我还不能发布图像):
看起来该块执行第一个命令,但随后只将其余命令打印到屏幕上。我对此感到非常困惑。这里有一个函数,它会在给出有效响应之前调用自身
Function Get-Response {
Param ($Main_Prompt)
switch (Read-Host $Main_Prompt) {
1 {1}
2 {2}
3 {3}
4 {4}
5 {5}
"q" {'q'}
Default {Get-Response @PSBoundParameters}
}
}
$Value = Get-Response -Main_Prompt 'Do Something'
Write-Verbose "Value: $Value given" -Verbose
我认为您正在寻找的最好是这样做(将代码从158行减少到43行) 创建一个空数组,并定义应用程序路径(或者将其移动到交换机,当您看到交换机时,这可能对您更有意义)。在开关中,创建用于定义应用程序标题和该应用程序的安装程序路径的对象。然后执行ForEach循环,选择唯一的对象并安装它们:
$ToInstall = @()
$App1 = [PSCustomObject]@{Name="App1 Name";Path="$InstallSource\Folder1\file.msi";Args="/I ""$InstallSource\Folder1\file.msi"" /qn-!"}
$App2 = [PSCustomObject]@{Name="App2 Name";Path="$InstallSource\Folder2\file.msi";Args="/I ""$InstallSource\Folder2\file.msi"" /qb"}
$App3 = [PSCustomObject]@{Name="App3 Name";Path = "$InstallSource\Folder3\file.msi";Args="/I ""$InstallSource\Folder3\file.msi"" /qn"}
$App4 = [PSCustomObject]@{Name="App4 Name";Path = "$InstallSource\Folder4\file.msi";Args="/I ""$InstallSource\Folder4\file.msi"" /qb-!"}
$App5 = [PSCustomObject]@{Name="App5 Name";Path = "$InstallSource\Folder5\file.msi";Args="/I ""$InstallSource\Folder5\file.msi"" /qn"}
$App6 = [PSCustomObject]@{Name="App6 Name";Path = "$InstallSource\Folder6\file.msi";Args="/I ""$InstallSource\Folder6\file.msi"" /qb-! REBOOT=ReallySuppress"}
$App7 = [PSCustomObject]@{Name="App7 Name";Path = "$InstallSource\Folder7\file.msi";Args="/I ""$InstallSource\Folder7\file.msi"" /qr"}
# Main Level Menu Choices
switch (Read-Host $Main_Prompt)
{
{$_ -eq 1 -or $_ -eq 5} {$ToInstall += $App1}
{$_ -eq 2 -or $_ -eq 5} {$ToInstall += $App2}
{$_ -eq 3 -or $_ -eq 5} {$App3,$App6,$App7|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}}
{$_ -eq 4 -or $_ -eq 5} {$App4,$App6,$App7|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}}
{!($_ -ieq "q")} {$ToInstall += $App5}
{$_ -ieq "q"} {Write-host "Exiting!";Exit}
}
# Install each app selected
ForEach($App in ($ToInstall |Sort Name)){
Write-Host $App.Name
if (Test-Path $App.Path) {
If($App.Name -eq "App3 Title"){Do stuff to stop service}
$processInfo = New-Object System.Diagnostics.ProcessStartInfo "msiexec.exe"
$processInfo.arguments = $App.args
$processMSI = New-Object System.Diagnostics.Process
$processMSI.StartInfo = $processInfo
$processMSI.Start() | Out-Null
$processMSI.WaitforExit()
if ($processMSI.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processMSI.ExitCode "Exiting!"
exit
}
}
Else {
Write-host "Installer does not exist. Exiting!"
exit
}
}
编辑:将对象创建移动到切换之前,插入代码以检查指定应用程序安装上的服务
Edit2:更新了
$processInfo.arguments=
行,并修复了$App3
-$App7
行。此外,我们还重新介绍了如何将应用程序添加到$ToInstall
数组中,以避免重复,并在ForEach循环中对安装顺序进行排序。我很确定这与问题的要求相反。当你给它任何不是1-5或qt的东西时,脚本就中断了。这是一个循环,就像OP-ask一样,只是没有使用Do/While语句完成。函数将一直调用自身,直到收到有效输入。哪一部分适合你?我在几个控制台(PowerShell V3)上进行了测试,没有出现问题。默认设置是捕获任何错误响应。这不是循环,而是递归函数。在您添加@PSBoundParameters
之前,它确实断了。但问题中没有提到任何关于等待有效响应的内容……当然,这是一个递归函数,但它仍然会提示用户输入,直到给出与可用选择匹配的响应。这看起来很棒,@TheMadTechnician。我将把它放到我的测试环境中,并让您知道它是如何工作的。另外,如果我想添加一些代码来测试某个服务是否正在运行,然后将其关闭(我已经有了相关的工作代码),但只针对该循环中的一个安装选项,我该如何做呢?我假设这与安装某个应用程序有关。我要做的是在ForEach循环中添加If($App.Name-eq“which option's title”){在安装之前检查服务并停止它}
,我可以做到。此外,每个应用程序的参数都不同。我是否也应该将它们设置为$ToInstall数组下的变量,并将它们放入开关块中?是的,只需向自定义对象添加另一个属性,例如[PSCustomObject]@{Name=“Name”;Path=$1Path;Args=“/I”“$($App.Path)”“/qr”}
(注意双引号,这是用反勾转义它们的替代方法)令人惊叹的。还需要一种方法来区分.exe和.msi的路径。这将改变代码块的工作方式。如果它们是.exe,则ProcessInfo目标将是exe而不是msiexec.exe。考虑一个带有另一个变量的简单if语句。。。是否有方法选择路径的最后三个字符作为测试,然后将进程目标更改为path变量或msiexec.exe?如果您不打算至少按照我提到的方式执行,则应将所有退出
,替换为中断
。除非你喜欢每次去测试你的脚本。如果你喜欢那样的事情…谢谢你的提示。我不使用ISE,但它可能是一个更好的方法来使用中断。好的,你在这里完全改变了上下文。您将从具有属性的对象变为字符串。另外,您完全忽略了将$ToInstall声明为数组的第一行,因此将其设置为字符串,然后将每个新字符串添加到现有字符串的末尾。我通过将管道删除为“Select-Unique”使其正常工作。再次感谢!
$ToInstall = @()
$App1 = [PSCustomObject]@{Name="App1 Name";Path="$InstallSource\Folder1\file.msi";Args="/I ""$InstallSource\Folder1\file.msi"" /qn-!"}
$App2 = [PSCustomObject]@{Name="App2 Name";Path="$InstallSource\Folder2\file.msi";Args="/I ""$InstallSource\Folder2\file.msi"" /qb"}
$App3 = [PSCustomObject]@{Name="App3 Name";Path = "$InstallSource\Folder3\file.msi";Args="/I ""$InstallSource\Folder3\file.msi"" /qn"}
$App4 = [PSCustomObject]@{Name="App4 Name";Path = "$InstallSource\Folder4\file.msi";Args="/I ""$InstallSource\Folder4\file.msi"" /qb-!"}
$App5 = [PSCustomObject]@{Name="App5 Name";Path = "$InstallSource\Folder5\file.msi";Args="/I ""$InstallSource\Folder5\file.msi"" /qn"}
$App6 = [PSCustomObject]@{Name="App6 Name";Path = "$InstallSource\Folder6\file.msi";Args="/I ""$InstallSource\Folder6\file.msi"" /qb-! REBOOT=ReallySuppress"}
$App7 = [PSCustomObject]@{Name="App7 Name";Path = "$InstallSource\Folder7\file.msi";Args="/I ""$InstallSource\Folder7\file.msi"" /qr"}
# Main Level Menu Choices
switch (Read-Host $Main_Prompt)
{
{$_ -eq 1 -or $_ -eq 5} {$ToInstall += $App1}
{$_ -eq 2 -or $_ -eq 5} {$ToInstall += $App2}
{$_ -eq 3 -or $_ -eq 5} {$App3,$App6,$App7|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}}
{$_ -eq 4 -or $_ -eq 5} {$App4,$App6,$App7|%{If(!($ToInstall -contains $_)){$ToInstall += $_}}}
{!($_ -ieq "q")} {$ToInstall += $App5}
{$_ -ieq "q"} {Write-host "Exiting!";Exit}
}
# Install each app selected
ForEach($App in ($ToInstall |Sort Name)){
Write-Host $App.Name
if (Test-Path $App.Path) {
If($App.Name -eq "App3 Title"){Do stuff to stop service}
$processInfo = New-Object System.Diagnostics.ProcessStartInfo "msiexec.exe"
$processInfo.arguments = $App.args
$processMSI = New-Object System.Diagnostics.Process
$processMSI.StartInfo = $processInfo
$processMSI.Start() | Out-Null
$processMSI.WaitforExit()
if ($processMSI.ExitCode -ne 0) {
Write-Host "The Installer generated error code:" $processMSI.ExitCode "Exiting!"
exit
}
}
Else {
Write-host "Installer does not exist. Exiting!"
exit
}
}