Start-Process -Wait is NOT waiting - powershell

When I search this site, others have suggested using options like -NoNewWindow However, that doesnt seem to help.
I am writing a script that installs SCCM. It is quite a long install (about 5 min) and even though I have the -Wait, it literally continues on throughout the remainder of the script.
Start-Process -FilePath C:\temp\SCCM_Client\ccmsetup.exe -ArgumentList SMSSITECODE=PCM -Wait
Write-Host "Verify SCCM client install after reboot"`r`n -ForegroundColor Green
It runs the ccmsetup.exe then after about 5 seconds or so, it continues on to the next line. I check task manager and ccmsetup.exe is CLEARLY still running.
So you guys think Im having a problem because this is an installer and not a program? (this command works just fine if I Start-process notepad.exe; it wont continue on until I close notepad) That's the only thing I can think of that's different
Thanks for any help!

I had this problem, ccmsetup.exe spawns another process which does the work. So it's behaving as expected because the ccmsetup.exe spawned by powershell has finished.
To get around this, my solution was to monitor the ccmsetup logs for an exit code.
Something like below.
$count = 1
$LogFilePath = 'C:\Windows\ccmsetup\Logs\ccmsetup.log'
do
{
Write-Output "Uninstalling Software Center - attempt $count"
Start-Process -FilePath C:\temp\SCCM_Client\ccmsetup.exe -ArgumentList SMSSITECODE=PCM
$count++
Start-Sleep 30
while ((Get-Content -Path $LogFilePath -Tail 1 | Select-String -Pattern "CcmSetup is exiting with return code" -SimpleMatch) -eq $null)
{
#Check every 10 seconds for an exit code
Start-Sleep 10
}
} until((Get-Content $LogFilePath -Tail 1 -Wait | Select-String -pattern "CcmSetup is exiting with return code 0" -SimpleMatch -Quiet) -or $count -gt 3)
I'm not in work at the moment, so don't have access to the actual portion of code - I'll try to update this tomorrow with the final version I used.
The Start-sleep 30 was required to prevent it checking the log prematurely and using an old exit code.

My solution to this particular problem was to use SCCM to build a batch file that actually works and runs separately from SCCM. It's an utterly insane solution.. but welcome to coding, I guess.
Write-Output "MsiExec.exe /x{5974413A-8D95-4D64-B9EE-40DF28186445} /qn" | Out-File -Encoding ASCII -FilePath mcafee-removal.bat
Write-Output "MsiExec.exe /x{377DA1C7-79DE-4102-8DB7-5C2296A3E960} /qn" | Out-File -Encoding ASCII -FilePath mcafee-removal.bat -Append
Write-Output "MsiExec.exe /x{820D7600-089E-486B-860F-279B8119A893} /qn" | Out-File -Encoding ASCII -FilePath mcafee-removal.bat -Append
Write-Output "MsiExec.exe /x{B16DE18D-4D5D-45F8-92BD-8DC17225AFD8} /qn" | Out-File -Encoding ASCII -FilePath mcafee-removal.bat -Append
Write-Output """%programfiles%\McAfee\Agent\x86\frminst.exe"" /remove=agent /silent" | Out-File -Encoding ASCII -FilePath mcafee-removal.bat -Append
Start-Process -FilePath "cmd.exe" -ArgumentList '/c .\mcafee-removal.bat'
The batch file behaves exactly the way you'd expect it to. SCCM's weird C++ implementation of powershell parsing ... yeah, not so much.
Hope this helps someone someday.

Related

Can you save an expression as a variable

Is it possible to save an expression such as this.
Out-File -FilePath "C:\StartupInfo.txt" -Append
To variable, I was hoping to do something like this
$logfile = Out-File -FilePath "C:\StartupInfo.txt" -Appened
I would use the variable to log the work in a text file that is done through out the script.
Write-OutPut "$ComputerName work completed." | $logFile

Powershell Trasnscript - Output to a file and NOT display in console

I am writing a PowerShell script to gather general information on our servers. I wrote the script so that it outputs to a file called output.txt via PowerShells Start-Transcript cmdlet. Output works fine. However I just want the output in the file and not displayed on the console.
I have been looking and attempting to see if Start-Transcription can suppress the console output but I have not found anything.
This is a very cut down version of the code I am using-
Start-Transcript -path "Path\To\Output\File.txt"
$servers = Get-Content -path "Path\To\Servers\List\file.txt"
foreach ($server in $servers)
{
net view
net use
net session
}
Stop-Transcript
It all outputs to the file correctly but I just would like it to NOT display the script/command results in the console. If that is possible.
Would this work?
$FilePath = 'Path\To\Output\File.txt'
net view | Out-File -FilePath $FilePath
net use | Out-File -FilePath $FilePath -NoClobber -Append
net session | Out-File -FilePath $FilePath -NoClobber -Append
Or bundle it:
Invoke-Command {net view ; net use ; net session} | Out-File -FilePath $FilePath -NoClobber -Append
EDIT based on comment (but written freely from memory on an iphone so maybe minor mistakes):
To run this remotely against a list of servers you first enable Powershell remoting on the servers, many ways to do it and here is one to run in a local powershell session on each server (Runas Admin):
winrm quickconfig
Then, assuming they all have the same login, you can:
$Cred = Get-Credential
$Servers = ’server1.example.com’,’server2.example.com’
Invoke-Command -ComputerNames $Servers -Credential $Cred -ScriptBlock {
Do stuff
Do some other stuff
} | Out-File -FilePath $FilePath -NoClobber -Append
Results are returned as an array so if you want to separate the output per server you could try:
$a = Invoke-Command [...]etc but skip the |Out-File
then do some loop which in essence does this part, giving you the manual way here:
$a[0] | Out-File -FilePath $FilePath1 -NoClobber -Append #result from first computer
$a[1] | Out-File -FilePath $FilePath2 -NoClobber -Append #result from second computer
...
and an example loop:
$a | Foreach-Object {$_ | Out-File -FilePath $Path -Append -NoClobber}
And to read the servernames from a text file, one servername per line:
$Servers = Get-Content -Path ’C:\temp\example.txt’
Invoke-Command -ComputerName $Servers [...]etc

Wait for file to finish writing before performing next script [duplicate]

I have a PowerShell 1.0 script to just open a bunch of applications. The first is a virtual machine and the others are development applications. I want the virtual machine to finish booting before the rest of the applications are opened.
In bash I could just say "cmd1 && cmd2"
This is what I've got...
C:\Applications\VirtualBox\vboxmanage startvm superdooper
&"C:\Applications\NetBeans 6.5\bin\netbeans.exe"
Normally, for internal commands PowerShell does wait before starting the next command. One exception to this rule is external Windows subsystem based EXE. The first trick is to pipeline to Out-Null like so:
Notepad.exe | Out-Null
PowerShell will wait until the Notepad.exe process has been exited before continuing. That is nifty but kind of subtle to pick up from reading the code. You can also use Start-Process with the -Wait parameter:
Start-Process <path to exe> -NoNewWindow -Wait
If you are using the PowerShell Community Extensions version it is:
$proc = Start-Process <path to exe> -NoNewWindow -PassThru
$proc.WaitForExit()
Another option in PowerShell 2.0 is to use a background job:
$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Besides using Start-Process -Wait, piping the output of an executable will make Powershell wait. Depending on the need, I will typically pipe to Out-Null, Out-Default, Out-String or Out-String -Stream. Here is a long list of some other output options.
# Saving output as a string to a variable.
$output = ping.exe example.com | Out-String
# Filtering the output.
ping stackoverflow.com | where { $_ -match '^reply' }
# Using Start-Process affords the most control.
Start-Process -Wait SomeExecutable.com
I do miss the CMD/Bash style operators that you referenced (&, &&, ||). It
seems we have to be more verbose with Powershell.
Just use "Wait-process" :
"notepad","calc","wmplayer" | ForEach-Object {Start-Process $_} | Wait-Process ;dir
job is done
If you use Start-Process <path to exe> -NoNewWindow -Wait
You can also use the -PassThru option to echo output.
Some programs can't process output stream very well, using pipe to Out-Null may not block it.
And Start-Process needs the -ArgumentList switch to pass arguments, not so convenient.
There is also another approach.
$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
Including the option -NoNewWindow gives me an error: Start-Process : This command cannot be executed due to the error: Access is denied.
The only way I could get it to work was to call:
Start-Process <path to exe> -Wait
The question was asked long ago, but since answers here are kind of references, I may mention an up to date usage. With the current implementation of PowerShell (it's 7.2 LTS as of writing) you can use && as you would do in Bash.
Conditionally execute the right-hand side pipeline based on the success of the left-hand side pipeline.
# If Get-Process successfully finds a process called notepad,
# Stop-Process -Name notepad is called
Get-Process notepad && Stop-Process -Name notepad
Further info on documentation
Taking it further you could even parse on the fly
e.g.
& "my.exe" | %{
if ($_ -match 'OK')
{ Write-Host $_ -f Green }
else if ($_ -match 'FAIL|ERROR')
{ Write-Host $_ -f Red }
else
{ Write-Host $_ }
}
Building upon #Justin & #Nathan Hartley 's answers:
& "my.exe" | Out-Null #go nowhere
& "my.exe" | Out-Default # go to default destination (e.g. console)
& "my.exe" | Out-String # return a string
the piping will return it in real-time
& "my.exe" | %{
if ($_ -match 'OK')
{ Write-Host $_ -f Green }
else if ($_ -match 'FAIL|ERROR')
{ Write-Host $_ -f Red }
else
{ Write-Host $_ }
}
Note: If the executed program returns anything other than a 0 exitcode, the piping will not work. You can force it to pipe with redirection operators such as 2>&1
& "my.exe" 2>&1 | Out-String
sources:
https://stackoverflow.com/a/7272390/254276
https://social.technet.microsoft.com/forums/windowsserver/en-US/b6691fba-0e92-4e9d-aec2-47f3d5a17419/start-process-and-redirect-output-to-powershell-window
There's always cmd.
cmd /c start /wait notepad
Or
notepad | out-host

Printing with Powershell and files in folders

I have a script which does some onsite printing. It doesnt work too well at the moment as the below runs for various file types which are sent to a folder to print, but the problem is it will only print 1 document at a time.
Start-Process –FilePath “c:\tests\*.docx” –Verb Print
I had the idea of doing this to get around it:
get-ChildItem "C:\Tests\*.docx" | `
foreach-object {
start-process -verb Print
}
This doesnt seem to work though. So then i tried this:
get-childitem "C:\Tests\*.xlsx" | `
foreach-object {
Start-Process -Filepath "C:\Program Files\Microsoft Office\Office14\EXCEL.exe" –Verb Print }
Also no luck,
Returns this error:
Start-Process : This command cannot be run due to the error: No application is associated with the specified file for this operation.
I think i am maybe not visualing the process here. Any ideas at all anyone on how to achieve printing of every file in a folder via powershell?
Windows 7 64 bit and $PSVersion = 5.0
Thanks in advance
You are very close, start-process needs the full path and name of the file:
Get-ChildItem "c:\tests\*.docx" | ForEach-Object {start-process $_.FullName –Verb Print}
Using a foreach loop should help you too:
$files = Get-ChildItem "c:\tests\*.docx"
foreach ($file in $files){
start-process -FilePath $file.fullName -Verb Print
}

How to tell PowerShell to wait for each command to end before starting the next?

I have a PowerShell 1.0 script to just open a bunch of applications. The first is a virtual machine and the others are development applications. I want the virtual machine to finish booting before the rest of the applications are opened.
In bash I could just say "cmd1 && cmd2"
This is what I've got...
C:\Applications\VirtualBox\vboxmanage startvm superdooper
&"C:\Applications\NetBeans 6.5\bin\netbeans.exe"
Normally, for internal commands PowerShell does wait before starting the next command. One exception to this rule is external Windows subsystem based EXE. The first trick is to pipeline to Out-Null like so:
Notepad.exe | Out-Null
PowerShell will wait until the Notepad.exe process has been exited before continuing. That is nifty but kind of subtle to pick up from reading the code. You can also use Start-Process with the -Wait parameter:
Start-Process <path to exe> -NoNewWindow -Wait
If you are using the PowerShell Community Extensions version it is:
$proc = Start-Process <path to exe> -NoNewWindow -PassThru
$proc.WaitForExit()
Another option in PowerShell 2.0 is to use a background job:
$job = Start-Job { invoke command here }
Wait-Job $job
Receive-Job $job
Besides using Start-Process -Wait, piping the output of an executable will make Powershell wait. Depending on the need, I will typically pipe to Out-Null, Out-Default, Out-String or Out-String -Stream. Here is a long list of some other output options.
# Saving output as a string to a variable.
$output = ping.exe example.com | Out-String
# Filtering the output.
ping stackoverflow.com | where { $_ -match '^reply' }
# Using Start-Process affords the most control.
Start-Process -Wait SomeExecutable.com
I do miss the CMD/Bash style operators that you referenced (&, &&, ||). It
seems we have to be more verbose with Powershell.
Just use "Wait-process" :
"notepad","calc","wmplayer" | ForEach-Object {Start-Process $_} | Wait-Process ;dir
job is done
If you use Start-Process <path to exe> -NoNewWindow -Wait
You can also use the -PassThru option to echo output.
Some programs can't process output stream very well, using pipe to Out-Null may not block it.
And Start-Process needs the -ArgumentList switch to pass arguments, not so convenient.
There is also another approach.
$exitCode = [Diagnostics.Process]::Start(<process>,<arguments>).WaitForExit(<timeout>)
Including the option -NoNewWindow gives me an error: Start-Process : This command cannot be executed due to the error: Access is denied.
The only way I could get it to work was to call:
Start-Process <path to exe> -Wait
The question was asked long ago, but since answers here are kind of references, I may mention an up to date usage. With the current implementation of PowerShell (it's 7.2 LTS as of writing) you can use && as you would do in Bash.
Conditionally execute the right-hand side pipeline based on the success of the left-hand side pipeline.
# If Get-Process successfully finds a process called notepad,
# Stop-Process -Name notepad is called
Get-Process notepad && Stop-Process -Name notepad
Further info on documentation
Taking it further you could even parse on the fly
e.g.
& "my.exe" | %{
if ($_ -match 'OK')
{ Write-Host $_ -f Green }
else if ($_ -match 'FAIL|ERROR')
{ Write-Host $_ -f Red }
else
{ Write-Host $_ }
}
Building upon #Justin & #Nathan Hartley 's answers:
& "my.exe" | Out-Null #go nowhere
& "my.exe" | Out-Default # go to default destination (e.g. console)
& "my.exe" | Out-String # return a string
the piping will return it in real-time
& "my.exe" | %{
if ($_ -match 'OK')
{ Write-Host $_ -f Green }
else if ($_ -match 'FAIL|ERROR')
{ Write-Host $_ -f Red }
else
{ Write-Host $_ }
}
Note: If the executed program returns anything other than a 0 exitcode, the piping will not work. You can force it to pipe with redirection operators such as 2>&1
& "my.exe" 2>&1 | Out-String
sources:
https://stackoverflow.com/a/7272390/254276
https://social.technet.microsoft.com/forums/windowsserver/en-US/b6691fba-0e92-4e9d-aec2-47f3d5a17419/start-process-and-redirect-output-to-powershell-window
There's always cmd.
cmd /c start /wait notepad
Or
notepad | out-host