I am writing a powershell script that allows Logstash to run for a certain amount of time and then stops the program by process id. Here is a basic version of the code:
$logstashFilepath = "C:\Users\emckenzie\Downloads\logstash-5.3.2\logstash-5.3.2\bin\logstash.bat"
$logstashArguments = "-f C:\users\emckenzie\Downloads\logstash-5.3.2\logstash-5.3.2\test_loader.conf"
$logstashprocess = Start-Process -FilePath $logstashFilepath -ArgumentList $logstashArguments -PassThru
$logstashid = $logstashprocess.Id
Write-Host $logstashid
Start-Sleep -s 60
Stop-Process $logstashid
In this test case Logstash only writes from stdin to stdout. When I run this program the output looks like this:
17120
Stop-Process : Cannot find a process with the process identifier 17120.
At C:\Users\emckenzie\Documents\Loaders\testLoader.ps1:13 char:13
+ Stop-Process <<<< $logstashid
+ CategoryInfo : ObjectNotFound: (17120:Int32) [Stop-Process], ProcessCommandException
+ FullyQualifiedErrorId : NoProcessFoundForGivenId,Microsoft.PowerShell.Commands.StopProcessCommand
Is this a problem in Logstash or Powershell?
Any help would be much appreciated!
I ended up using NSSM to start Logstash as a service instead, and I control NSSM through Powershell.
# You must configure testlogstash using the GUI NSSM provides
$logstashFilepath = "C:\Users\emckenzie\Downloads\nssm-2.24\nssm-
2.24\win64\nssm.exe"
$start = "start testlogstash"
$stop = "stop testlogstash"
$logstashprocess = Start-Process -FilePath $logstashFilepath -ArgumentList
$start -PassThru
Start-Sleep -s 60
Start-Process -FilePath $logstashFilepath -ArgumentList $stop
After reading the comments on the original post I believe there is a solution for you. If you know the program running that needs to stay running until the job is finished:
$logstashprocess = Start-Process -FilePath $logstashFilepath -ArgumentList $logstashArguments -PassThru
$logstashid = $logstashprocess.Id
$Global:IsRunning = $false
Do {
$process = Get-Process -processname "NameOfProcessHere"
If ($process){
$IsRunning = $true
}else{
$IsRunning = $false
}
} While ($IsRunning -eq $true)
Stop-Process -processname "$logstashid"
Let me know if this is helpful, or if i'm not understanding the question.
Related
$GAMCheck = invoke-command -ScriptBlock { C:\GAMADV-XTD3\GAM.exe version checkrc }
If ($GAMCheck) {
$current = $GAMCheck.split(":")[19].Trim()
$latest = $GAMCheck.split(":")[21].Trim()
If ($LASTEXITCODE -eq 1) {
Try {
$NeedUpGradeCode = $LASTEXITCODE
$client = new-object System.Net.WebClient
$client.DownloadFile("https://github.com/taers232c/GAMADV-XTD3/releases/download/v$latest/GAMadv-xtd3-$latest-windows-x86_64.msi", "C:\Temp\GAMadv-xtd3-$latest-windows-x86_64.msi")
Start-Process -Filepath "C:\Temp\GAMadv-xtd3-$latest-windows-x86_64.msi" -ArgumentList "/passive" | Wait-Process -Timeout 75
Remove-Item "C:\Temp\GAMadv-xtd3-$latest-windows-x86_64.msi"
$GAMCheck = $null
$GAMCheck = invoke-command -ScriptBlock { C:\GAMADV-XTD3\GAM.exe version checkrc }
$newCurrent = $GAMCheck.split(":")[19].Trim()
$resultsarray = [PSCustomObject]#{
CurrentVersion = $current
LatestVersion = $latest
NeedUpgradeCode = $NeedUpGradeCode
Upgraded = $true
NewCurrent = $newCurrent
AfterUpgradeCode = $LASTEXITCODE
}
}
Catch {
Write-Warning "Problem with site or command. Maybe go to https://github.com/taers232c/GAMADV-XTD3/releases and download the current GAM and then install GAM in C:\GAMADV-XTD3\ again"
}
}
}
lately I have been noticing that the | Wait-process 75 above is causing an error.
If I run the command with out it everything is fine.
Is there another way to wait for the install ?
To launch a process with Start-Process and wait for it to exit, use the -Wait switch.
Piping a Start-Process call to Wait-Process would only work as intended if you included the -PassThru switch, which makes Start-Process - which by default produces no output - emit a System.Diagnostics.Process instance representing the newly launched process, on whose termination Wait-Process then waits.
Note that, surprisingly, the behavior of these two seemingly equivalent approaches is not the same, as discussed in GitHub issue #15555.
I'm running an exe from a PowerShell script. This executable writes its logs to a log file. I would like to continuously read and forward the logs from this file to the console while the executable is running.
Currently, I'm starting the exe like this:
$UNITY_JOB = Start-Job
-ScriptBlock { & "C:\Program Files\Unity\Hub\Editor\2019.2.11f1\Editor\Unity.exe" $args | Out-Null }
-ArgumentList $UNITY_ARGS
If I just do Get-Content $LOG_PATH -Wait at this point, I cannot detect when the exe terminates and the script blocks indefinitely.
If I start a second job for the logs, the output is not sent to the console:
$LOG_JOB = Start-Job -ScriptBlock { Get-Content $LOG_PATH -Wait }
(I need "real time" output, so I don't think Receive-Job would work)
I'd use a loop which ends when the job's status is Completed:
# Just to mock the execution
$extProgram = Start-Job -ScriptBlock { Start-Sleep -Seconds 30}
$file = 'C:\path\to\file.txt'
do {
cls
Get-Content $file -Tail $host.ui.RawUI.WindowSize.Height
Start-Sleep -Seconds 5 # Set any interval you need
} until ((Get-Job -Id $extProgram.id).State -eq "Completed")
I'm trying to get one master PowerShell script to run all of the others while waiting 30-60 seconds to ensure that the tasks are completed. Everything else I tried wouldn't stop/wait for the first script and its processes to complete before going through all the others at the same time and would cause a restart automatically.
Main script, run as admin:
$LogStart = 'Log '
$LogDate = Get-Date -Format "dd-MM-yyy-hh-mm-ss"
$FileName = $LogStart + $LogDate + '.txt.'
$scriptList = #(
'C:\Scripts\1-OneDriveUninstall.ps1'
'C:\Scripts\2-ComputerRename.ps1'
);
Start-Transcript -Path "C:\Scripts\$FileName"
foreach ($script in $scriptList) {
Start-Process -FilePath "$PSHOME\powershell.exe" -ArgumentList "-Command '& $script'"
Write-Output "The $script is running."
Start-Sleep -Seconds 30
}
Write-Output "Scripts have completed. Computer will restart in 10 seconds."
Start-Sleep -Seconds 10
Stop-Transcript
C:\Scripts\3-Restart.ps1
1-OneDriveUninstall.ps1:
Set-ItemProperty -Path REGISTRY::HKEY_LOCAL_MACHINE\Software\Microsoft\windows\CurrentVersion\Policies\System -Name ConsentPromptBehaviorAdmin -Value 0
taskkill /f /im OneDrive.exe
C:\Windows\SysWOW64\OneDriveSetup.exe /uninstall
2-ComputerRename.ps1:
$computername = Get-Content env:computername
$servicetag = Get-WmiObject Win32_Bios |
Select-Object -ExpandProperty SerialNumber
if ($computername -ne $servicetag) {
Write-Host "Renaming computer to $servicetag..."
Rename-Computer -NewName $servicetag
} else {
Write-Host "Computer name is already set to service tag."
}
The log file shows:
Transcript started, output file is C:\Scripts\Log 13-09-2019-04-28-47.txt.
The C:\Scripts\1-OneDriveUninstall.ps1 is running.
The C:\Scripts\2-ComputerRename.ps1 is running.
Scripts have completed. Computer will restart in 10 seconds.
Windows PowerShell transcript end
End time: 20190913162957
They aren't running correctly at all though. They run fine individually but not when put into one master script.
PowerShell can run PowerShell scripts from other PowerShell scripts directly. The only time you need Start-Process for that is when you want to run the called script with elevated privileges (which isn't necessary here, since your parent script is already running elevated).
This should suffice:
foreach ($script in $scriptList) {
& $script
}
The above code will run the scripts sequentially (i.e. start the next script only after the previous one terminated). If you want to run the scripts in parallel, the canonical way is to use background jobs:
$jobs = foreach ($script in $scriptList) {
Start-Job -ScriptBlock { & $using:script }
}
$jobs | Wait-Job | Receive-Job
Here's the code
function RunPowershellAsAdmin($CommandToBeExecuted)
{
If (-NOT ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole] "Administrator"))
{
#$arguments = "& '" + $myinvocation.mycommand.definition + "'"
Start-Process powershell -Verb runAs -ArgumentList "$CommandToBeExecuted" -Verbose
}
}
RunPowershellAsAdmin("& { Import-Module WebAdministration; if(Test-Path 'IIS:\Sites\$Website_Name') { Remove-WebSite -Name '$Website_Name'; } }")
-Verb and -RedirectStandardOutput are not in the same parameter, so i can not use -RedirectStandardOutput to get process output as per answer from this link.
I want to run the process in a hidden window, wait for it to return and get the error, output and exit code.
Is there any other solution?
Thanks in advance.
If you need access to both RunAs in addition to redirecting any of the standard streams, you'll need to use the System.Diagnostics.Process and System.Diagnostics.ProcessStartInfo classes directly. More information on how to handle the redirected stream can be found on MSDN.
$startInfo = new-object System.Diagnostics.ProcessStartInfo
$startInfo.FileName = "powershell"
$startInfo.Arguments = "& { Import-Module WebAdministration; ... }"
$startInfo.Verb = "runas"
$startInfo.RedirectStandardOutput = $true
$process = [System.Diagnostics.Process]::Start($startInfo)
$output = $process.StandardOutput.ReadToEnd()
$process.WaitForExit()
This question is a repost from https://social.msdn.microsoft.com/Forums/windowsdesktop/en-US/365ca396-400d-4401-bd7f-05d5d6c22cc8/powershells-startsleep-causes-memory-growth?forum=winserverpowershell
I did not get a working answer there so try if this forum can help. This is blocking my work.
Here is my two powershell scripts. The first one is to create an event source and the second one is to generate events using 4 threads. Start-Sleep is used in each thread to control the event generation rate. If I remove the Start-Sleep, then the powershell memory usage is constant, otherwise it grows fast until all system memory is used and the system becomes extremely slow.
Is this a known issue? Any workaround? Appreciate any clue.
# use this script to create channel and source if they does not exist.
$logName = "TestLog"
$sourceName = "TestSource"
New-EventLog -LogName $logName -Source $sourceName
# maximumSize's max is 4G.
Limit-EventLog -LogName $logName -OverflowAction OverWriteOlder -RetentionDays 30 -MaximumSize 3080000KB
Event generating script:
# use this script to generate events in TestLog channel.
Param(
[int]$sleepIntervalInMilliSeconds = 0
)
$eventGenScript = {
$logName = "TestLog"
$sourceName = "TestSource"
while($true) {
Write-EventLog -LogName $logName -Source $sourceName -Message "perfLog" -EventId 0 -EntryType information
Start-Sleep -ms $sleepIntervalInMilliSeconds
}
}
$threadCount = 4
for($i=0; $i -lt $threadCount; $i++)
{
Start-Job $eventGenScript
}
read-host "type a key to exit. You need to wait for some time for threads to exit."
It's not that Start-Sleep has a memory leak, you're calling it with an invalid Parameter (-ms) and the process memory of the jobs is filling up with error messages, because you keep calling the (invalid) statement in an infinite loop.
Demonstration:
PS C:\> Start-Sleep -ms 100
Start-Sleep : A parameter cannot be found that matches parameter name 'ms'.
At line:1 char:13
+ Start-Sleep -ms 100
+ ~~~
+ CategoryInfo : InvalidArgument: (:) [Start-Sleep], ParameterBindingException
+ FullyQualifiedErrorId : NamedParameterNotFound,Microsoft.PowerShell.Commands.StartSleepCommand
Also, you're defining $sleepIntervalInMilliSeconds outside the scriptblock, but try to use it inside the scriptblock, which won't work, because the variable is undefined in the scope of the scriptblock. This is why your problem remained despite the correct advice you got on the Microsoft forums.
PS C:\> $ms = 100
PS C:\> $job = Start-Job -ScriptBlock { Start-Sleep -Milliseconds $ms }
PS C:\> $job | Wait-Job | Receive-Job
Cannot validate argument on parameter 'Milliseconds'. The argument is null,
empty, or an element of the argument collection contains a null value.
Supply a collection that does not contain any null values and then try the
command again.
+ CategoryInfo : InvalidData: (:) [Start-Sleep], ParameterBindingValidationException
+ FullyQualifiedErrorId : ParameterArgumentValidationError,Microsoft.PowerShell.Commands.StartSleepCommand
+ PSComputerName : localhost
You have three options to deal with this:
Define the variable inside the script block:
PS C:\> $job = Start-Job -ScriptBlock {
>> $ms = 100
>> $ms
>> Start-Sleep -Milliseconds $ms
>> }
>>
PS C:\> $job | Wait-Job | Receive-Job
100
Use the using scope modifier to get the local variable:
PS C:\> $ms = 100
PS C:\> $job = Start-Job -ScriptBlock {
>> $using:ms
>> Start-Sleep -Milliseconds $using:ms
>> }
>>
PS C:\> $job | Wait-Job | Receive-Job
100
Pass the variable into the scriptblock as an argument:
PS C:\> $ms = 100
PS C:\> $job = Start-Job -ScriptBlock {
>> param($ms)
>> $ms
>> Start-Sleep -Milliseconds $ms
>> } -ArgumentList $ms
>>
PS C:\> $job| Wait-Job | Receive-Job
100
Bottom line: replace
Start-Sleep -ms $sleepIntervalInMilliSeconds
with
Start-Sleep -Milliseconds $using:sleepIntervalInMilliSeconds
and the problem will disappear.