Stop and then start a process in powershell - powershell

I would like to stop/kill a certain process and then start it again after I am done doing what I have to do.
This is what I already have.
Clear-host
$processes = Get-Process devenv
$processes.Count
if($processes.Count -gt 1)
{
$i = 0
Write-host "There are multiple processes for devenv."
foreach($process in $processes)
{
$i++
$i.ToString() + '. ' + $process.MainWindowTitle
}
$in = Read-host "Give a number of the process to kill: "
write-host
write-host "killing and restarting: " + $processes[$in-1].MainWindowTitle
$processes[$in-1].Kill()
$processes[$in-1].WaitForExit()
$processes[$in-1].Start()
}
else
{
write-host "something else"
}
But the Start needs some parameter which I thought I could get from the process. But I'm not really sure I know what to give it.

The $processes[$in-1].Start() will not work. You need to capture the processinfo you are killing and start the same app again. You can get the process binary and commandline information using Win32_Process WMI class.
For example,
Clear-host
$processes = Get-Process notepad
$processes.Count
if($processes.Count -gt 1)
{
$i = 0
Write-host "There are multiple processes for notepad."
foreach($process in $processes)
{
$i++
$i.ToString() + '. ' + $process.MainWindowTitle
}
$in = Read-host "Give a number of the process to kill: "
write-host
write-host "killing and restarting: " + $processes[$in-1].MainWindowTitle
#Get the process details
$procID = $processes[$in-1].Id
$cmdline = (Get-WMIObject Win32_Process -Filter "Handle=$procID").CommandLine
$processes[$in-1].Kill()
$processes[$in-1].WaitForExit()
}
In the above example, I am using WMI to get the commandline information for a process selected. If that were a notepad process with some open text file, the commandline for that process would look like "C:\WINDOWS\system32\NOTEPAD.EXE" C:\Users\ravikanth_chaganti\Desktop\debug.log
Now, all you need to do is: Invoke that commandline somehow (this part is not there in example I wrote). A very blunt way to do that is:
Start-Process -FilePath $cmdline.Split(' ')[0] -ArgumentList $cmdline.Split(' ')[1]
But, in your case, there may not be any argument list.
Hope this gives you an idea. Other PowerShell experts may have a different & efficient approach. This is just a quick hack.

Related

Powershell Date

I have a personalized powershell profile, on my $PROFILE I have added the Get-Date command and it gives me the date at the moment the script is executed, I wanted to know if it is possible to have the time updated every second or five and still being able to use the powershell, Ive tried a lot of possibilities but cant find one that works, if thi is not possible is there anything similar?
thanks.
ive tried a lot of whiles, jobs ...
here some examples of what I have tried:
$script = {
while ($true) {
Write-Output (Get-Date -Format G)
Start-Sleep -Seconds 1
}
}
Start-Job -ScriptBlock $script
while ($true) {
Write-Host (Get-Date -Format G) -NoNewline
Start-Sleep -Seconds 1
Write-Host "`r" -NoNewline
}
You could modifiy the prompt function for this. This way it won't interfere with commands output and the terminal.
Check current definition with:
(Get-Command prompt).ScriptBlock
It should be something like this:
"PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) ";
Then modify it for current session only (just paste and execute in terminal):
function prompt {
"$(get-date -f 'G') PS $($executionContext.SessionState.Path.CurrentLocation)$('>' * ($nestedPromptLevel + 1)) ";
}
prompt before:
PS C:\Users\username>
prompt after:
2023-02-09 16:13:13 PS C:\Users\username>
If it works as expected, you can place new function definition in $PROFILE. This way you'll always have the time of all meaningful events, i.e. every command/script start and completion.

Exit from while loop but keep shell open: powershell

For learnig Powershell I've wrote a script that choose a film randomly from a list, until the list runs out, but, at the end of list I want that the shell keeps open... I've tried many options Break/Return/exit... nothing to do... tried to add the last if condition in to the while condition but, not works, at the end of list an error message says that the array is empty/null, or shell quits after last film...
Yes, I can put a pause instead of return, for that script would be ok, but it doesn't seems an "elegant" solution...
The icing on the cake would be a better option to trim the extension from film name..! Because in my mode, a film name that finish with a "m", "k", or "v", these chars are trimmed out toghether with the ".mkv" expression! :)
Can someone drive me please?
This is the code:
$LastFile = (Get-Item "F:\DATA\Films list\*" | Where-Object {$_.Name -match "Title"} | sort LastWriteTime | select -last 1).FullName
$TitleList = Get-Content $LastFile | where { $_ -notmatch "4K" }
$TitleList.Count
$Listed = New-Object System.Collections.ArrayList
Foreach ( $film in $TitleList ) {
$Listed.Add("$film") | out-null
}
$RandomFilm = Get-Random -InputObject $Listed
Write-Host $RandomFilm.Trim('.mkv') -ForegroundColor Green
$Listed.Remove("$RandomFilm")
$TitleCount = 1
echo "_________________________________________________________________________________________"
echo "`n`n"
$Answer = Read-Host "RETURN for new film, q for quitting..."
while ( "q" -notcontains $Answer) {
echo "`n`n"
$RandomFilm = Get-Random -InputObject $Listed
if ( $RandomFilm -ne $null ) {
Write-Host $RandomFilm.Trim('.mkv') -ForegroundColor Green
$Listed.Remove("$RandomFilm")
$TitleCount++
$TitleCount
if ( $TitleCount -gt $TitleList.Count ) {
Write-Host "END OF LIST..." -ForegroundColor red
Return
}
else {
echo "_________________________________________________________________________________________"
echo "`n`n"
$Answer = Read-Host "RETURN for new film, q for quitting..."
}
}
}
Several ways of doing this. If you are running your script with powershell then $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown")at the end of the script is a good option (this will not work with powershell_ise tho). In ISE you can add a read-host at the end.
If you are running your script from a shortcut you can add -NoExit: powershell.exe -NoExit -file .\script.ps1
The icing on the cake would be a better option to trim the extension from film name..! Because in my mode, a film name that finish with a "m", "k", or "v", these chars are trimmed out toghether with the ".mkv" expression! :)
Im not quite sure I understood this part, could you please elaborate.

Foreach loop not running through list of entries

I've tried searching this multiple times but havent resolved the issue. I have a list of servers in $mediaagentlist, the foreach loop is supposed to run through each one and get the state of certain services:
$mediaagentlist = "cs0400ma01
cs0400ma02"
[string]$Commcell_Input = $args[0]
$MAChoice = $args[1]
if ($MAChoice -eq $null)
{
Write-Output "No media agent was specified, running against all MAs in the Commcell..."
#Run this loop for each MA which is stored in Mediaagentlist
foreach ($Mediaagent in $Mediaagentlist)
{
Write-Output $Mediaagent
$GxCLMgrS_State = Invoke-Command -ComputerName $Mediaagent {Get-Service -name "GxClMgrS(Instance001)"}
$GXMMM_State = Invoke-Command -ComputerName $Mediaagent {Get-Service -name "GXMMM(Instance001)"}
$GxCVD_State = Invoke-Command -ComputerName $Mediaagent {Get-Service -name "GxCVD(Instance001)"}
Write-Output "Client manager service state: " $GxCLMgrS_State.Status
Write-Output "Media manager mount service state: " $GXMMM_State.Status
Write-Output "Communications service state: " $GxCVD_State.Status
}
}
How do I correct this so the for each loop runs through the list of entries in $mediaagentlist and runs the code for each server?
Thanks in advance!
It looks like your issue is here
$mediaagentlist = "cs0400ma01
cs0400ma02"
It looks like your missing a closing qoute per word and a comma
try
$mediaagentlist = "cs0400ma01", "cs0400ma02"
or
$mediaagentlist = #("cs0400ma01", "cs0400ma02")
Or you can define a multi-line string, then split on new lines, if there's a reason you needed the input to be a single multi-line string:
$mediaagentlist = "cs0400ma01
cs0400ma02" -split '[\r\n]+'

How to configure a timeout for Read-Host in PowerShell

Like I said, this code works in PowerShell version 2, but not in PowerShell version 5.
function wait
{
$compte = 0
Write-Host "To continue installation and ignore configuration warnings type [y], type any key to abort"
While(-not $Host.UI.RawUI.KeyAvailable -and ($compte -le 20))
{
$compte++
Start-Sleep -s 1
}
if ($compte -ge 20)
{
Write-Host "Installation aborted..."
break
}
else
{
$key = $host.ui.rawui.readkey("NoEcho,IncludeKeyup")
}
if ($key.character -eq "y")
{Write-Host "Ignoring configuration warnings..."}
else
{Write-Host "Installation aborted..."
}}
The official documentation or Read-Host -? will tell that it's not possible to use Read-Host in that manner. There is no possible parameter to tell it to run with some kind of timeout.
But there are various other questions detailing how to do this in PowerShell (usually utilizing C#).
The idea seems to be to check whenever the user pressed a key using $Host.UI.RawUI.KeyAvailable and check that for the duration of your timeout.
A simple working example could be the following:
$secondsRunning = 0;
Write-Output "Press any key to abort the following wait time."
while( (-not $Host.UI.RawUI.KeyAvailable) -and ($secondsRunning -lt 5) ){
Write-Host ("Waiting for: " + (5-$secondsRunning))
Start-Sleep -Seconds 1
$secondsRunning++
}
You could use $host.UI.RawUI.ReadKey to get the key that was pressed. This solution probably would not be acceptable if you need more complex input than a simple button press. See also:
Windows PowerShell Tip of the Week - Pausing a Script Until the User Presses a Key
PowerTip: Use PowerShell to Wait for a Key Press (Hey, Scripting Guy!)
Seth, thank you for your solution. I expanded on the example you provided and wanted to give that back to the community.
The use case is a bit different here - I have a loop checking if an array of VMs can be migrated and if there are any failures to that check the operator can either remediate those until the checks clear or they can opt to "GO" and have those failing VMs excluded from the operation. If something other than GO is typed state remains within the loop.
One downside to this is if the operator inadvertently presses a key the script will be blocked by Read-Host and may not be immediately noticed. If that's a problem for anyone I'm sure they can hack around that ;-)
Write-Host "Verifying all VMs have RelocateVM_Task enabled..."
Do {
$vms_pivoting = $ph_vms | Where-Object{'RelocateVM_Task' -in $_.ExtensionData.DisabledMethod}
if ($vms_pivoting){
Write-Host -ForegroundColor:Red ("Some VMs in phase have method RelocateVM_Task disabled.")
$vms_pivoting | Select-Object Name, PowerState | Format-Table -AutoSize
Write-Host -ForegroundColor:Yellow "Waiting until this is resolved -or- type GO to continue without these VMs:" -NoNewline
$secs = 0
While ((-not $Host.UI.RawUI.KeyAvailable) -and ($secs -lt 15)){
Start-Sleep -Seconds 1
$secs++
}
if ($Host.UI.RawUI.KeyAvailable){
$input = Read-Host
Write-Host ""
if ($input -eq 'GO'){
Write-Host -ForegroundColor:Yellow "NOTICE: User prompted to continue migration without the blocked VM(s)"
Write-Host -ForegroundColor:Yellow "Removing the following VMs from the migration list"
$ph_vms = $ph_vms | ?{$_ -notin $vms_pivoting} | Sort-Object -Property Name
}
}
} else {
Write-Host -ForegroundColor:Green "Verified all VMs have RelocateVM_Task method enabled."
}
} Until(($vms_pivoting).Count -eq 0)
Also note that all this $Host.UI stuff doesn't work from the Powershell ISE.
To find out from within a script you could test for $Host.Name -eq "ConsoleHost". When true you can use the code from this topic. Otherwise you could use $Host.UI.PromptForChoice or any other way of showing a dialog box. With System.Windows.Forms.Timer you can then set a timer, and code to close the dialog box or form can be run when it expires.

Powershell Novice - Get all httpd processes and loop through them

How do I get all processes in power-shell and loop through them and restart them if their memory has reached a X threshold?
For example, I know of this command
PS C:\Users\me> gwmi -ComputerName "localhost" -Class Win32_PerfFormattedData_PerfProc_Process -Filte
r "name like 'httpd%'"
This will give me all httpd processes (httpd, httpd#1, ...).
I would like to loop through these and check if they memory consumption threshold has been reached and if so, restart that process.
My question is primarily how to write the loop, not about how to stop and restart the service. Please explain as I never wrote a power-shell script before.
UPDATE:
I added more information to better explain what my issue is. Below code has comments showing where is see problems (PROBLEM 1 and PROBLEM 2):
$ServiceExe="httpd#1"
$ServiceEXE2="httpd"
# Service to restart ('short' service name from Service's property)
$Service="httpd.exe"
# Working set threshold of 0.2 GB
$Threshold = 200000000
# Get service processes 'httpd' and 'httpd#1'
$Process = Get-WmiObject -ComputerName "localhost" -Class Win32_PerfFormattedData_PerfProc_Process -Filter "Name='$ServiceExe'"
$Process2 = Get-WmiObject -ComputerName "localhost" -Class Win32_PerfFormattedData_PerfProc_Process -Filter "Name='$ServiceExe2'"
# Get working set memory usage for both processes and convert to GB
$wsm = $Process.WorkingSet /1024/1024/1024;
$wsm2 = $Process2.WorkingSet /1024/1024/1024;
$thgb = $Threshold/1024/1024/1024
# Format to 3 dec places
$Fwsm = $("{0:0.000}" -f $wsm);
$Fthgb =$("{0:0.000}" -f $thgb);
echo $("WorkingSet : "+ $Fwsm + " GB / Threshold : " + $Fthgb + " GB.") >> C:\temp\pslog.txt;
if($Process.workingset -gt $Threshold)
{
#PROBLEM 1: THIS WILL ONLY EXECUTE ONCE PROCESS CAPS MAX MEMORY (2GB)
stop-process -name $Service -force
stop-Service apache2.2;
start-Service apache2.2;
echo "Restarted" >> C:\temp\pslog.txt;
}
else
{
#PROBLEM 2: THIS WILL NEVER EXECUTE FOR SOME REASON
$delta = $("{0:0.000}" -f ($thgb - $wsm));
echo $("No Restarting as " + $delta + " GB remains.");
}
I was thinking to replace variables holding process 'httpd' and 'httpd#1' with a single set that I could loop through but realized that Apache may have more than 2 'httpd' processes (i.e. 'httpd', 'httpd#1', 'httpd#2', 'httpd#3', 'httpd#4', ... ). Plus, I discovered that I have the 2 problems marked in the code in update above.
Any idea what I am doing wrong in the code above and how to rewrite it to use a loop to loop through all apache processes that might be initiated, not just 2 like in example above.
UPDATE 2:
So, I rewrote my ps script and I got most of it working but I am running into an issue, hopefully last one.
In my power-shell script, I have defined variable that has my service name like this:
$ServiceName = "APACHESRV[DBx14]";
APACHESRV[DBx14] service exists in my Windows->Services.
Then in my power-shell script, I use stop-service, start-service to start the service:
echo $("Stopping: " + $ServiceName) >> C:\temp\pslog.txt;
stop-Service $ServiceName;
echo $("Starting: " + $ServiceName) >> C:\temp\pslog.txt;
start-Service $ServiceName;
$stamp = Get-Date
echo $($stamp + " Started: " + $ServiceName) >> C:\temp\pslog.txt;
This echoes everything properly and power-shell shows no errors (red-lines), however my service is not started.
Much appreciated
At this point, I think you are just missing your loop. You are retrieving the two process objects, but you are only working on the first one. I took the liberty of restructuring it to work on all http processes. Try this:
# Working set threshold of 0.2 GB
$Threshold = 200000000
$thgb = $Threshold/1024/1024/1024
$Fthgb =$("{0:0.000}" -f $thgb)
# Get service processes 'httpd' and 'httpd#1' Get-WmiObject -ComputerName "localhost" -Class Win32_PerfFormattedData_PerfProc_Process | where { $_.Name -like "httpd*" } | foreach {
$procobj = $_
# Get working set memory usage for both processes and convert to GB
$wsm = $procobj.WorkingSet /1024/1024/1024;
# Format to 3 dec places
$Fwsm = $("{0:0.000}" -f $wsm)
echo $("WorkingSet : "+ $Fwsm + " GB / Threshold : " + $Fthgb + " GB.") >> C:\temp\pslog.txt;
if($procobj.workingset -gt $Threshold)
{
stop-process -name $procobj.Name -force #check this, probably wrong name
stop-Service apache2.2;
start-Service apache2.2;
echo "Restarted" >> C:\temp\pslog.txt;
}
else
{
$delta = $("{0:0.000}" -f ($thgb - $wsm));
echo $("No Restarting as " + $delta + " GB remains.");
}
}
This should do it...
ps | where { $_.name -like "httpd*" } | foreach {
# interrogate the System.Diagnostics.Process object in $_ and do stuff with it
$_
}
Get all the httpd processes with the Get-Process cmdlet, loop through them with the ForEach-Object cmdlet:
# Set a threshold, eg. 120MB
$Threshold = 120 * 1024 * 1024
# Loop through the processes
Get-Process httpd* | ForEach-Object {
if($_.WS -gt $Threshold){
# Working set exceeded threshold, restart relevant service
}
}