Reputation: 8101
I've created a script that uses psexec to call another script which calls psexec to run a command line program of mine.
The reason for so many calls to psexec and other scripts is solely so that my PHP script doesn't have to wait for the process to finish before finishing it's output to the browser.
Is there a way I can do this without needing to use psexec? I'm having issues with psexec so I'd like to just completely remove it from my program.
I'm running Windows 2008
EDIT: I changed the title, I guess this would be a more accurate title. I found out the If a program is started with this function, in order for it to continue running in the background, the output of the program must be redirected to a file or another output stream. Failing to do so will cause PHP to hang until the execution of the program ends.
on php.net's page on exec()
, but wasn't sure how to do that.
Upvotes: 3
Views: 13769
Reputation: 549
In my case it was required to start a php script to run in a background from a logic running as part of HTTP request, and without blocking the request.
After many tests with no success I found that using powershell Start-Process but (VERY IMPORTANT) without using RedirectStandardOutput, RedirectStandardError or UseNewEnvironment parameters of Start-Process command, provide the required behavior.
What was not working in my case:
Here is a sample if code that start the worker from within "php" script running in the scope of HTTP request.
if (StringUtils::isWindows()) {
// Make sure that PowerShell 7 is installed and location of 'pwsh' exe is in environment path.
$command = "pwsh " . INCLUDES_DIR . "/bin/message_worker.ps1 $workerId $cacheDir $script";
} else {
$command = INCLUDES_DIR . "/bin/message_worker.sh $workerId $cacheDir $script";
}
exec($command, $output);
$info = implode("", $output);
service_watchdog(__FUNCTION__, "Starting worker.\n$info");
Here is a sample Powershell script that invoked from the code running inside Http scope.
Set-Variable -Name "worker_id" -Value $args[0]
Set-Variable -Name "cache_dir" -Value $args[1]
Set-Variable -Name "script" -Value $args[2]
Set-Variable -Name "context_file" -Value $cache_dir\context\$worker_id
Write-Output "Running a worker for handling service-bus message"
Write-Host "worker_id = $worker_id"
Write-Host "cache_dir = $cache_dir"
Write-Host "script = $script"
Write-Host "context_file = $context_file"
$processOptions = @{
FilePath = "php"
ArgumentList = "$script $context_file"
# Note!!! Do not use RedirectStandardOutput, RedirectStandardError, UseNewEnvironment, for process to work in the background.
# Using these parameters cause process to block the Http request that initiated it.
}
Start-Process @processOptions
I tested also the solution that uses popen(..) and it works too and simpler. In this case I can use classing command line script and call it like this:
$command = "start ". INCLUDES_DIR . "/bin/message_worker.cmd $workerId $cacheDir $script > Nul";
popen( $command, 'r' );
And the command line script:
echo "Running worker for handling background messages"
set worker_id=%1%
set cache_dir=%2%
set script=%3%
set context_path=%cache_dir%\context\%worker_id%
set log_file=%cache_dir%\workers.log
php %script% %context_path% 1> %log_file% 2>&1
exit
Upvotes: 0
Reputation: 1161
Using start /B has certain limitations when calling scripts that are spawning sub-processes. The following method spawns a sub-process using PowerShell to handle such cases:
function execInBackgroundWindows($filePath, $workingDirectory, $arguments)
{
$cmd = "powershell.exe Start-Process -FilePath $filePath -WorkingDirectory $workingDirectory -ArgumentList '$arguments'";
shell_exec($cmd);
}
execInBackgroundWindows('curl.exe','c:\temp','-X POST -H "Content-Type: application/json" -d @test.json http://127.0.0.1:4000/myapp');
Upvotes: 1
Reputation: 1043
$command = 'start /B program.exe > NUL';
pclose( popen( $command, 'r' ) );
For more info: http://humblecontributions.blogspot.com/2012/12/how-to-run-php-process-in-background.html
Upvotes: 4
Reputation: 36318
Include a redirection in the command line:
exec('program.exe > NUL')
or you could modify your program to explicitly close standard output, in C this would be
CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
It is possible (the documentation doesn't say) that you might need to redirect/close both the standard output and standard error:
exec('program.exe > NUL 2> NUL')
or
CloseHandle(GetStdHandle(STD_OUTPUT_HANDLE));
CloseHandle(GetStdHandle(STD_ERROR_HANDLE));
Upvotes: 6
Reputation: 50031
Try the Windows 'start
' command: http://technet.microsoft.com/en-us/library/cc770297%28WS.10%29.aspx
Upvotes: 2