PowerShell timing loop doesn't complete, hangs at random spot - powershell

I've created a very simple function in a powershell module which should capture a keystroke if a key is pressed within a short time window.
function get-mycommand{
$count=0
$sleepTimer=2 #in milliseconds
while ($count -le 100)
{
if($host.UI.RawUI.KeyAvailable) {
$key = $host.UI.RawUI.ReadKey('NoEcho, IncludeKeyDown')
return $key.character
} else {
$count++
Write-Host $count
Start-Sleep -m $sleepTimer
}
}
return "fail"
}
function test-get-command{
$myvalue = get-mycommand
Write-Host 'return ' $myvalue
}
This works fine so long as I press a key quickly. If I don't press a key then it will count up to some random number, usually less than 20, and then stop updating. It won't do anything until I press a key and it never times out. When I do press a key, even after several minutes it will return with the character. I never see the 'fail' message at all.
Using a longer timer interval doesn't change anything.I've tried a sleeptimer of 2, 20 & 200. They all behave the same.

Unfortunately, $host.UI.RawUI.KeyAvailable can have false positives in Windows PowerShell as well as in PowerShell (Core) up to at least v7.2.0-preview.9 (current as of this writing).
On Windows, it seemingly thinks a key is available even when there isn't, so that $key = $host.UI.RawUI.ReadKey('NoEcho, IncludeKeyDown') is executed, which indefinitely waits for an actual keypress.
In a regular console window this seems to happen after a few iterations at the latest.
In Windows Terminal, it seems to happen consistently on the first iteration, so that your function produces no output at all.
GitHub issue #959 seems relevant, although it relates to the PSReadLine module.
While unloading the module (Remove-Module PSReadLine) - which you wouldn't want to do in a interactive session - does fix the problem in regular console windows, it doesn't help in Windows Terminal.
Workaround:
Replace $host.UI.RawUI.KeyAvailable with [Console]::KeyAvailable
Note that this limits use to consoles (terminals), but your original code wouldn't work in the obsolescent ISE anyway, because $host.UI.RawUI.KeyAvailable there seemingly never reports a pending keypress.

Related

Is there a way to return to the command prompt temporarily

How would One allow users to "Pause the current Pipeline and return to the command prompt" and later resume in a powershell script?
I stumbled upon this line in a blog post about User Interaction in Powershell
$suspend = New-Object System.Management.Automation.Host.ChoiceDescription "&Suspend", "Pause the current pipeline and return to the command prompt. Type ""exit"" to resume the pipeline."
It was a mock option in a prompt imitating the appearance of a native command (Remove-Item). Lo and behold: That command actually Implements that behavior. Doing a quick Google Search, I did not find an implementation in a script.
You can use $Host.EnterNestedPrompt() to suspend the current operation to enter a "nested" prompt - execution will resume once you exit it (using either exit or $Host.ExitNestedPrompt()):
function f {
param([switch]$Intervene)
$abc = 123
if($Intervene.IsPresent){
$host.EnterNestedPrompt()
}
Write-Host "`$abc has value '$abc'"
}
Now try invoking the function with and without the -Intervene switch:
Mathias R. Jessen's helpful answer is undoubtedly the best solution for your use case.
Because there is functional overlap, let me offer a solution for on-demand debugging of a script or function, using the Wait-Debugger cmdlet, which also enters a nested prompt in the current scope, while offering additional, debugging-specific functionality to step through the code.
function foo {
'Entering foo.'
if (0 -eq $host.ui.PromptForChoice('Debugging', 'Enter the debugger?', ('&Yes', '&No'), 1)) {
Wait-Debugger
}
'Exiting foo.'
}
Executing foo presents a yes/no prompt for whether the debugger should be entered.
The debugging prompt can be exited with exit or simply c (one of the debugging-specific commands; h shows them all).

How do I get notified when a command is finished and Powershell is back to prompt?

I'm doing some testing job, and a part of it is to regularly write files to some hardware. The thing is, the write can take as short as 20 seconds, or as long as several minutes. Staring at the screen and waiting for it to be done is a huge waste of time, so I'm wondering if there is a way to get notified (like making a beep sound by using [console]::beep() ) when the command is done and the Powershell is back to prompt?
You can edit your prompt function:
$function:prompt = #"
{0}
[console]::beep()
"# -f $function:prompt.ToString()
A more concise version (compliments of mklement0) if you don't mind semi-colon delimited commands:
$function:prompt = "$function:prompt; [console]::beep()"
PowerShell Alert when command is finished :
"Answer" | out-file response.txt
[console]::beep(500,300)
Powershell > [console]::beep for 1-Minute :
[console]::beep(500,60000)
Also - Try using the "Asterisk" Alert :
[System.Media.SystemSounds]::Asterisk.Play()

Powershell ISE functions caching, how to get rid of?

I've been struggling the last 2 hours on trying to kill caching of functions in the Powershell ISE. Here's a minimalistic example to demonstrate the issue:
function myFunction {
" Monday "
}
displays: Monday
function myFunction {
" Tuesday "
} # edited, saved..
still displays: Monday
UPDATE: figured it out..! Will still post as some others may benefit.. As it happens, Powershell is sensitive to the location of the declaration of the function vs. the call to it. It has to be AFTER.. It would be less treacherous if it simply errored out with something like "Object not found", but no, it executes nicely, if it were not for what you think is an annoying caching behavior.
I've been scripting on Linux with Perl, which is insensitive to the functions declaration/call therefore lowering my guard..
Saving a file does not re-execute it. Unless you reload the function binding, then it will remain in that session unless you restart the session (CTRL+T for a new session tab in ISE).
function myFunction { 'Monday' }
F5
PS /> myFunction
Monday
function myFunction { 'Tuesday' }
F5
PS /> myFunction
Tuesday

What is the command and syntax for breaking/stopping a program in QBASIC?

I am currently writing a QBASIC program that runs an indefinite loop (while loop). However, if a certain condition is met, I want to exit the program. What command do I use, and also what is the syntax.
Thanks
ENDexits program, and clears all variables, which frees up memory.
STOPexits program, but retains the value of all variables, which makes it possible (in certain versions of QB) to continue execution at another point, by choosing Set next statement from the Debugmenu, and then Startfrom the Runmenu. END has the same effect as STOP + choosing Restart from the Runmenu once the program has terminated.
If you have a loop, and want to exit the program from inside it, you may use either
DO
IF condition THEN EXIT DO
LOOP
END
or
DO
IF condition THEN END
LOOP
You're looking for the END or SYSTEM statement. For example:
PRINT "Hello World!"
END
PRINT "This won't be printed."
If you're using regular old QBASIC/QuickBASIC, then you can ignore all of the QB64 details on the linked pages and just use either SYSTEM or END. Both will do the same thing for the most part.1
If you're using FreeBASIC, it's recommended to use END instead of SYSTEM since some things won't get cleaned up properly when you use SYSTEM. See SYSTEM for more information pertaining to FreeBASIC if that's what you're using.
1 The END statement when running the program using QB.EXE /RUN PROGRAM.BAS will print "Press any key to continue" before exiting to the QB/QBASIC environment. The SYSTEM statement when run the same way will simply return you to the DOS shell without any need for a key press. Also, typing SYSTEM in the "Immediate Window" of the QB/QBASIC environment will exit the environment and return to the DOS shell. Otherwise the two statements behave exactly the same in QB/QBASIC, whether for standalone (compiled) programs or .BAS modules.
You can keep any condition according to the need of your program. For eg:
CLS
LET a = 5
WHILE a > 0
PRINT a;
a = a - 1
WEND
END
Here, in the program while wends executes itself until a = 0. This will not run an infinite loop.
The answer is
exit();
to exit the program.

In Windows PowerShell, How Can You Set the Maximum CPU % for the Script to Use?

I am looking to limit the percentage of the CPU time used by a PowerShell process to a certain number -- for the sake of argument, let's imagine it to be 13%.
Other options that are not precisely what I need:
1) Setting priority.
2) Setting CPU affinity.
Basically, we have monitoring software which complains if the total CPU usage gets too high. We have a daily process that sets this off -- mostly harmlessly, but too many false positives in a monitoring system and people become inured to warnings/errors when we do not wish that.
The process itself gets lsass.exe very excited, too, as it runs, and other processes happen, as well.
I do not know PowerShell and am attempting to fix Somebody Else's Powershell. Obviously, a ground-up rewrite would be nice at some future point, but for now, bells are ringing and annoying people.
What you're asking for isn't really possible. The Windows kernel is in charge of scheduling the CPU -- and rightfully so. (I for one don't want to return to real-mode DOS).
The best you can do is insert a long Sleep() in between each line of the script. But there's no guarantee that any particular Powershell cmdlet / function / etc will throttle itself the way you want. The Windows API calls that ultimately execute each statement's dirty work certainly won't.
Ironically, Raymond touched on this topic just a few days ago: http://blogs.msdn.com/oldnewthing/archive/2009/07/27/9849503.aspx
My real suggestion is to modify your script so it looks like:
try {
Stop-CpuMonitor
# ...the current script contents...
}
finally {
Start-CpuMonitor
}
From my experience, there's not a way to stipulate what percentage of the CPU Powershell will get to use. I think the best course of action would be to set the priority of Powershell to Low to allow other tasks/programs to go first. I think the first post has a decent suggestion of using the pauses (I'd upvote, but I'm below the 15 reputation points to do so) and that's something you might look into, but I don't think it will give you the control you're looking for. Sorry my answer is more of a suggestion than a resolution.
/matt
I sincerely doubt there is a particularly simple way to accomplish what you are asking. If CPU concern is a big deal, I would combine a couple of tactics and let the scheduler take care of the rest - it is actually pretty good at managing the load.
The suggestion using ThreadPriority is a bit problematic as PowerShell with spawn each new command in a new Thread, which you could get around by having everything encapsulated on a single line, a cmdlet, or a function of some sort. Better to send the whole powershell process to idle:
Get-Process -name powershell | foreach { $_.PriorityClass = "Idle" }
Note: that will send ALL powershell instances to idle, which may not be the desired effect. And certainly doesn't prevent a script from boosting its own priority either.
Also, as mentioned here - littering your code with sleep commands can be an efficient way to ensure other processes have ample CPU time to process their code. Even 50-100ms is almost an eternity to a processor.
[System.Threading.Thread]::Sleep(50)
Between these two tactics your script will run when it is available, and graciously bow to other CPU demands as they arise.
While I know of no way to actually limit the usage of any process to a particular amount of CPU consumption by percentage, I figured maybe one could alter the priority of the PowerShell thread. So I wrote a quick script and ran a test.
The result was that in both "Normal" and "Lowest", the time taken was about the same. Looking at the CPU meter widget, the CPU usage taken was approximately 27%, but I was running on a quad core box, so that's not surprising. It was taking all of one CPU in both cases. My machine wasn't doing much else at the time, so that was the other 2%, I guess.
Perhaps the results will vary on a busier machine.
Here's the script:
function Spin
{
param($iterations)
for($i = 0; $i -lt $iterations; ++$i)
{
# Do nothing
}
}
$thread = [System.Threading.Thread]::CurrentThread
Write-Host $thread.Priority
$start = [DateTime]::Now
Write-Host "[$start] Start"
Spin 10000000
$end = [DateTime]::Now
Write-Host "[$end] End"
$span = $end - $start
Write-Host "Time: $span"
$thread.Priority = "Lowest"
Write-Host $thread.Priority
$start = [DateTime]::Now
Write-Host "[$start] Start"
Spin 10000000
$end = [DateTime]::Now
Write-Host "[$end] End"
$span = $end - $start
Write-Host "Time: $span"
$thread.Priority = "Normal"
And here's the result:
Normal
[08/06/2009 08:12:38] Start
[08/06/2009 08:12:55] End
Time: 00:00:16.7760000
Lowest
[08/06/2009 08:12:55] Start
[08/06/2009 08:13:11] End
Time: 00:00:16.8570000
Notice also that in the documentation for Thread.Priority it states:
Operating systems are not required to honor the priority of a thread.
A possibility may be using process lasso. I have had nothing but positive results over automatic throttling of processes not excluded by me when the CPU usage surpasses a certain percentage.
Keep in mind that there is a fairly limited free version that you can use to test, if it works for you.
PS: I am not working for anybody. I'm just offering a program that has worked for me in the past.
Use AddType on a C# script literal, which again uses P/Invoke to call the Win32 functions CreateJobObject, SetInformationJobObject (passing a JOBOBJECT_CPU_RATE_CONTROL_INFORMATION) and AssignProcessToJobObject. It will probably be easier to write a wrapper program in C that sets the limits and spawn off the PowerShell script in such a job.