How to keep a powershell script running 24/7 - powershell

I have a PowerShell script that needs to be restarted when it dies for whatever reason, be it a crash, a self exit or after a system reboot...
How can I, from a bat or another powershell script, see to it that if it is not running, it will be started again...
i.e. how can I find out if it is already running from another script?
I know I can make one powershell script start the active one and simply have it loop a new start as long as it doesnt exit with a specific error... but then THAT scripts need to be seen to :D So we are back to the original quesiton, how do I keep THAT script running 24/7?
do
{
$date = Get-Date -format "yyyy-MM-ddTHH:mm:ss"
"$($date) Repeat : Restarting Worker, LastExitCode $($LastExitCode)." | Out-File D:\IDM\Worker\Worker.LOG -width 180 -append
powershell -File "D:\IDM\Scripts\Worker.ps1"
sleep 10
}
while ($LastExitCode -ne $null)

I would just use scheduled tasks. There are plenty of options in there to help you do what you want. You can run the script every five minutes and have it do enough loops to take up that time and quit:
$now = Get-Date
while ($now.AddMinutes(5) -lt (Get-Date)){
...work...
}
Or you could even have it write a flag file every time the loop works and have any new process check that file to see if there hasn't been activity on it. If there's been no activity:
$workFlag = Get-Item C:\work.flg
$cutOff = (Get-Date).AddMinutes(-5)
if ($workFlag.LastWriteTime -gt $cutOff){
New-Item -force -path C:\work.flg
...work loop..
}

Related

How to kill/stop a powershell process on no output for some period of time

Creating processes using powershell start-process command , which will executes the processes sequentially and output will be displayed in console. below is the code snippet:
foreach($module in $selectedModules){
$filepath = $basePath + $module
Start-Process -FilePath $filepath -ArgumentList $cmdArgs -Wait -NoNewWindow
}
If one process stuck, need to kill it so that next iteration process will start.If console output not updated for specified time range say 15 minutes, we can consider that process got hang and stop it. so that next process will begin.Tried with wait-process, but it needs predefined timeout value in place. any better way to find out stuck process ? or need to time with no output.

Start-Job: Call another script within a ScriptBlock (Loop)

I have another problem, which I can't solve by myself even using the search..
I have a script that starts Robocopy as job, and a second job that watches this script if it's running, if not, send an e-mail.
Now I want to add in the watch script part to start the whole script again. (loop)
& "$PSScriptRoot\Sync_Start&Watch.ps1"
Robocopy-Job:
Script_Block_Sync = {
param ($rc_logfile, $rc_source, $rc_destination)
robocopy $rc_source $rc_destination /MIR /SEC /SECFIX /COPYALL /m /r:5 /Mon:1 /Mot:1 /unilog:$rc_logfile
}
Start-Job -Name Robocopy_Sync -ScriptBlock $Script_Block_Sync -ArgumentList $rc_logfile, $rc_source, $rc_destination
Watch_Job:
$Script_Block_Check = {
param($MailParams, $mailbody_error, $PSScriptRoot)
while ((Get-Process Robocopy).Responding) {Start-Sleep -Seconds 30}
if (!(Get-Process Robocopy).Responding) {
Send-MailMessage #MailParams -body $mailbody_error
& "$PSScriptRoot\Sync_Start&Watch.ps1"
}
}
Start-Job -Name Robocopy_Check -ScriptBlock $Script_Block_Check -ArgumentList $MailParams, $mailbody_error, $PSScriptRoot
I've tried with $PSScriptRoot, with the full path and with separate $script variable. If I run only the line (F8) or the whole IF block (F8) the script starts running.
If it's not possible to start another script, maybe another job which starts the script?
Any idea what I missed, or is it still not possible?
Thank you for any help!
Best regards
After a week of searching and trying multiple variations I found a solution to get the wanted loop. Not what I actually want, but it works. If someone read this here and have another or even better idea, you're very welcome! My idea was, to start a script via Task Scheduler, that starts everything in background without showing "Running" state.
FYI: To my problem, I missed the point, that PS jobs only run in the session they started in, so everything works fine if I test the script in PS ISE, but not via Task Scheduler, because if the script (session) ends, the jobs started within the script ends too.
So to get a loop, I use the following code:
while (!(Get-Process Robocopy).Responding) {
$Script_Block {...}
Start-Job -Name Robocopy -ScriptBlock $Script_Block
Start-Sleep -Seconds 5
while ((Get-Process Robocopy).Responding) {
Start-Sleep -Seconds 60
}
Send-MailMessage ...
}

running a command repeatedly on a remote server

I wrote the following script to run a command on a remote server with 5 sec interval. The command inside $LogrCmd variable runs on a remote server to check if a particular service is up or down. I expect the script to poll the service every 5 seconds until the service is completely down. However the scripts exits out immediately even if the service is up.
$LogrCmd = get-content 'c:\temp\info.cfg' | select-string -Pattern cheetahdev
while (-not (Invoke-Command -ScriptBlock {& cmd.exe /c "$LogrCmd"})) {
## Wait a specific interval
Start-Sleep -Seconds 5
}
Here's the contents of the info.cfg file which runs against the remote host.
"C:\PWX\pwxcmd displaystatus -sv cheetahdev"
You would do better to use a do / while loop here instead of a while loop:
$LogrCmd = Get-Content 'c:\temp\info.cfg' | Select-String -Pattern cheetahdev
do {
cmd.exe /c "$LogrCmd"
## Wait a specific interval
Start-Sleep -Seconds 5
} while ( $LASTEXITCODE -eq 0 )
This will always run the command once, sleep, then check to see if the command succeeded. If the command succeeded it will continue the loop. Of course, you can tailor the while condition to check for other exit codes and conditions as well.
Note that for external commands it's best to rely on $LASTEXITCODE most of the time to check for command success, unless you need to parse the output of the command or something else less common.
Also note that by reading the full command from the file like that opens you up to code injection attacks by someone familiar with how to manipulate your info.cfg.

Run a external program with parameters and wait for it to end in Powershell

Actually, I've found so many solutions to this question, but none works.
The program I'd like to run in Powershell is Reaper - a Digital Audio Workstation, and I'm going to use its command line tool for batch-processing audio files within a PS script. The code related to Reaper is as below:
reaper -batchconvert $output_path\audio\Reaper_filelist.txt
I'm going to use the Start-Process with -wait parameter to allow my script to wait for it to end then go on to the next line of code which is a Rename-Item function.
ls $processed_audio_path | Rename-Item -NewName {$_.name.Replace("- ", "")}
If PS doesn't wait for the process to finish, then the next line would throw an error, something like "no such file was found in the directory".
The suggestions I've found are here, here, and here. But none of those work. The problem is that Reaper doesn't accept the argument to be added separately as:
$exe = "reaper"
$arguments = "-batchconvert $output_path\audio\Reaper_filelist.txt"
Start-Process -filepath $exe -argumentlist $arguments -wait
or:
Start-Process -filepath reaper -argumentlist "-batchconvert $output_path\audio\Reaper_filelist.txt" -Wait
or:
Start-Process -filepath reaper -argumentlist #("-batchconvert", "$output_path\audio\Reaper_filelist.txt") -Wait
It can only work without a problem as a whole block like the first code line above.
So what can I do now?
I've found a solution to this problem.
I think I need to describe more context about this. I always have Reaper launched with my Windows in the background, when the script calls the BatchConvert function of Reaper, it will fire up another instance of Reaper, so I got 2 instances of it when converting audio files. This - the instances of Reaper - could be a reliable condition to restrict the following code. I found something useful from here and here.
Finally, I got my code like this and it works:
# Batch converting through Reaper FX Chain
reaper -batchconvert $output_path\audio\Reaper_filelist.txt
while (#(Get-Process reaper).Count -eq 2){
Start-Sleep -Milliseconds 500
}
# Correct the Wrong file name produced by Reaper
ls $processed_audio_path | Rename-Item -NewName {$_.name.Replace("- ", "")}
As the one comment mentions, it could be that the process starts another process, causing powershell to move along in the script.
If that's the case, you could have a while statement to wait for the file to be created.
while (!(Test-Path "$output_path\audio\Reaper_filelist.txt")) { Start-Sleep 10 }

Powershell script run from task scheduler unable to open word document

I am trying to use a powershell script to get the wordcount from a number of word files and then output that to a csv file. This works as expected when run from the powershell prompt, and works when called from the cmd prompt directly or from inside a perl script, however the script fails to work when called as a scheduled task.
This is not an ExecutionPolicy issue causing the script to not run at all. The script is running and produces some output, but when launched from task scheduler it is unable to open any word documents.
The relevant powershell code is below:
$folderpath = "C:\some\where\*"
$fileTypes = "*.docx"
$word = New-Object -ComObject word.application
$word.visible = $false
Get-ChildItem -path $folderpath -recurse -include $fileTypes |
foreach-object `
{
$path = ($_.fullname).substring(0,($_.FullName).lastindexOf("."))
try {
$doc = $word.documents.open($_.fullname, $confirmConversion, $readOnly, $addToRecent, $passwordDocument)
} catch {
"FROM CATCH UNABLE TO OPEN $($_.fullname)" >> $wordCountFile
}
if($doc) {
$wordCount = $doc.ComputeStatistics("wdStatisticWords")
"$($_.name), $wordCount" >> $wordCountFile
$doc.close([ref]$false)
} else {
"UNABLE TO OPEN $($_.fullname)" >> $wordCountFile
}
} #end Foreach-Object
$word.Quit()
(Note that alternative methods people cite for getting the word count from word documents do not actually return the correct count as this method does when it works.)
When run as a scheduled task (set to run with highest privileges and as an administrator user) using:
cmd /c start PowerShell.exe -NoLogo -NonInteractive -ExecutionPolicy Bypass -File "C:\path\to\script\CountsInWords.ps1"
or when run with a similar command from a perl script launched by a scheduled task (my normal use case) and in both cases the task is set to run with highest privileges the powershell script does not work correctly.
The catch block is apparently never reached, as the print statement never makes it to the file, however the $doc is always null.
Additionally, when started as a task the script leaves a word process open and using a 100% cpu for 1 thread which will eventually cripple my machine.
To summarise:
run script as human (no matter how many levels of indirection) -> works perfectly
run script from task (as administrator user) -> script runs but cannot access word documents, also unable to stop word process despite always hitting the $word.Quit line.
EDIT: With #TheMadTechnician's advice about office first time startup for a new user requiring some details: On further inspection, looking at the processes in task manager, I don't see the word processes unless I click on "show processes from all users", but then they show up, but have the user listed as me. How can a process be both explicitly listed as me, but count as another user?
Attempting to set $word.visible = $true in the script didn't actually make anything appear when launched from task scheduler, so I don't know either how to verify that it is waiting for input, or how to give it that input as the correct user to make it go away...
You may be able to follow the solution named at the question How to run a Windows 2008 task from the scheduler with "interact with desktop".
Summary: If you have 64-bit Windows: Create %SystemRoot%\SysWOW64\config\systemprofile\Desktop. If you have 32-bit Windows, create %SystemRoot%\system32\config\systemprofile\Desktop.