I have a curious case that I cannot fathom the reason for...
Please know I am a novice to PowerShell.
I am working on a PowerShell menu system to help automate building out new computers in my environment. I have a PS1 file that holds the script for an app install. When I use the script to reference this I am able to run it and have no issue. However, when I try inserting this into a function and referencing it does not.
This works:
4 # Microsoft Office 32-bit
{
Write-Host "`nMicrosoft Office 32-bit..." -ForegroundColor Yellow
# {installMS32Bit}
Invoke-Expression "cmd /c start powershell -NoExit -File '\\**SERVERPATH**\menuItems\ms_office\32-bit\install.ps1'"
Start-Sleep -seconds 2
}
This does not:
function installMS32Bit(){
Invoke-Expression "cmd /c start powershell -NoExit -File '\\**SERVERPATH**\menuItems\ms_office\32-bit\install.ps1'"
}
}
4 # Microsoft Office 32-bit
{
Write-Host "`nMicrosoft Office 32-bit..." -ForegroundColor Yellow
{installMS32Bit}
Start-Sleep -seconds 2}
install.ps1 file:
# Copy MS Office uninstall and setup to local then run and install 32-bit Office
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\setup.exe' -Destination 'C:\temp\' -Force
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\uninstall.xml' -Destination 'C:\temp\' -Force
Copy-Item -Path '\\**SERVERPATH**\menuItems\ms_office\32-bit\Setup.exe' -Destination 'C:\temp' -Force
Invoke-Expression ("cmd /c 'C:\temp\setup.exe' /configure 'C:\temp\uninstall.xml'")
Start-Process -FilePath 'C:\temp\Setup.exe'
Secondary question and a little explanation for Invoke-Expression...
I like to see progress and like to have secondary windows open to monitor the new process being run. I was unable to find a solution with a persistent window that worked for me to do this without Invoke-Expression.
If there is a better way to do this in PowerShell I am all ears!
{installMS32Bit}
As Mathias points out in a comment on the question, this statement doesn't call your function, it wraps it in a script block ({ ... }
)[1], which is a piece of reusable code (like a function pointer, loosely speaking), for later execution via &
, the call (execute) operator.
To call your function, just use its name (by itself here, given that there are no arguments to pass): installMS32Bit
Invoke-Expression
should generally be avoided; definitely don't use it to invoke an external program, as in your attempts.
Additionally, there's generally no need to call an external program via cmd.exe
(cmd /c ...
), just invoke it directly.
For instance, replace the last Invoke-Epression
call from your question with:
# If the EXE path weren't quoted, you wouldn't need the &
& 'C:\temp\setup.exe' /configure 'C:\temp\uninstall.xml'
I like to see progress and like to have secondary windows open to monitor the new process being run. I was unable to find a solution with a persistent window that worked for me to do this without Invoke-Expression.
(On Windows), Start-Process
by default executes a console application in a new window (unless you specify -NoNewWindow
), asynchronously (unless you specify -Wait
).
You cannot pass a .ps1
script directly to Start-Process
(it will be treated like a document to open rather than an executable to call), but you can pass it to PowerShell's CLI via the -File
parameter:
Start-Process powershell.exe '-File install.ps1'
The above is short for:
Start-Process -FilePath powershell.exe -ArgumentList '-File install.ps1'
That is, PowerShell will execute the following in a new window:
powershell.exe -File install.ps1
[1] Since you're not assigning the script block being created to a variable, it is implicitly output (printed to the display, in the absence of a redirection); a script block stringifies by its literal contents, excluding the enclosing {
and }
, so string installMS32Bit
will print to the display.