Script to run a program at a certain CPU load - windows

I have tried for like 3 hours now, with multiple codes similar to this:
wmic cpu get loadpercentage > Load.txt
findstr "%random:~,1%" Load.txt > Load1.txt
set load=<Load1.txt
if %load%==" 2 7 " echo yes
pause
But they all run in to a similar problem, the output of wmic cpu get loadpercentage:
LoadPercentage
56
The format just doesn't allow it to be put into a variable, so I can't check it for anything. Perferably, I would like it to be done in Windows CMD and/or Powershell.
Thanks for the help!
EDIT:
Thanks to #lit for the code, here's my final code that works perfectly:
:: To find the "GUID" or the codes for each power plan, run the command in CMD "powercfg -list".
set HighPerformanceMode=8c5e7fda-e8bf-4a96-9a85-a6e23a8c635c
set PowerSaverMode=a1841308-3541-4fab-bc81-f71556f20b4a
:loop
SETLOCAL ENABLEDELAYEDEXPANSION
SET "load="
FOR /F "usebackq skip=1 tokens=*" %%f IN (`wmic cpu get loadpercentage`) DO (
IF "!load!" EQU "" (
set "load=%%~f"
)
)
if "%load%" geq "65" (
ping localhost -n 2 >nul
if "%load%" geq "65" (
%systemroot%\System32\powercfg.exe /setactive %HighPerformanceMode%
)
) else (
if "%load%" lss "25" (
ping localhost -n 2 >nul
if "%load%" lss "25" (
%systemroot%\System32\powercfg.exe /setactive %PowerSaverMode%
)
)
)
endlocal
ping localhost -n 3 > nul
goto loop
Make sure you change the HighPerformanceMode and PowerSaverMode to have your computer specific power plans. You can find the codes by doing powercfg -list in cmd.
I then made a separate short script that just has "C:\Load Batch\Load Batch.bat" in it, but you have to change it to wherever the main script is. Then I used a program called "BAT to EXE converter" and put it in Ghost Mode, and put the newly made .exe program into my startup folder.
EDIT 2:
I don't believe that my question is a duplicate, the linked question is about getting CPU and RAM usage for what appears to be just to view it, while my question is about getting the load percentage as a pure text form to be used in a script. I am aware of it only testing for one CPU core, but as one goes up it is very likely that the others have similar loads. I had searched this site for code that would separate the "LoadPercentage" text when wmic cpu get loadpercentage is ran, because I couldn't set it into a variable that way.

It is likely that the problem is that wmic output is in Unicode. How about going with PowerShell?
Get-CimInstance -ClassName CIM_Processor | select LoadPercentage
I am not sure from the question about what needs to run.
The typical fallback of cmd shell programmers is usually something like:
#ECHO OFF
SETLOCAL ENABLEDELAYEDEXPANSION
SET "X="
FOR /F "usebackq skip=1 tokens=*" %%f IN (`wmic cpu get loadpercentage`) DO (
IF "!X!" EQU "" (
set "X=%%~f"
)
)
ECHO X is %X%
EDIT:
Actually, there will be a LoadPercentage emitted for each core. You probably want the arithmetic mean (average) of them.
Get-CimInstance -ClassName CIM_Processor |
Measure-Object -Property LoadPercentage -Average |
Select Average
Doing this in a cmd script would involve summing the LoadPercentage values and dividing by the count of them.
SET /A TOTAL=0
SET /A CORE_COUNT=0
FOR /F "usebackq skip=1" %%t IN (`type NUL ^| wmic /node:"%SERVER_NAME%" cpu get loadpercentage ^| findstr .`) DO (
IF "%%t" NEQ "" (
SET /A TOTAL=!TOTAL! + %%t
)
SET /A CORE_COUNT=!CORE_COUNT! + 1
)
SET /A AVG_UTILIZATION=%TOTAL% / %CORE_COUNT%
ECHO Number of cores: %CORE_COUNT%
ECHO Total CPU utilisation: %TOTAL%
ECHO Average CPU utilisation: %AVG_UTILIZATION%%%

Although writing to a (temporary) file and reading it back with set /p is a possible and valid way, the usual way of getting the output of a command into a variable in cmd is a for /f loop:
for /f "" %%a in ('wmic cpu get loadpercentage /value ^|find "="') do set /a "%%a"
echo %loadpercentage%
As in lits answer, a find (or findstr) command is used to convert the Unicode output of wmic to a "cmd compatible" format.
I use the /value parameter to get an outputformat that's easier to parse and set /a to get a numeric value (no need to mess with spaces).
The benefit of using /value is, you can easily get more than one parameter from the same wmic command:
for /f "delims=" %%a in ('"wmic cpu get Caption,CurrentClockSpeed,ExtClock,L2CacheSize /value |findstr = "') do set "_%%a"
set _
Enclosing the complete command in quotes removes the need of escaping special chars. Of course this works only, if you don't need quotes in the commandstring itself.
(without quoting:
for /f "delims=" %%a in ('wmic cpu get Caption^,CurrentClockSpeed^,ExtClock^,L2CacheSize /value ^|findstr = ') do set "_%%a"
)

Related

How to get the number of physical and logical cores in a batch file on a dual socket machine?

WMIC CPU Get NumberOfCores,NumberOfLogicalProcessors gets me most of what I want, but how do I store the combined output into a variable?
for /f "delims=" %%a in ('"WMIC CPU Get NumberOfCores,NumberOfLogicalProcessors /value"') do set /a "_%%a"
set _
Works for single socket machines only. The WMIC command returns cpu info on separate lines, i.e.
>WMIC CPU Get NumberOfCores,NumberOfLogicalProcessors /value
NumberOfCores=24
NumberOfLogicalProcessors=48
NumberOfCores=24
NumberOfLogicalProcessors=48
#echo off
setlocal
set /A "_NumberOfCores=_NumberOfLogicalProcessors=0"
for /F "tokens=1,2 delims==" %%a in ('WMIC CPU Get NumberOfCores^,NumberOfLogicalProcessors /value') do if "%%b" neq "" set /A "_%%a+=%%b"
set _
Note that wmic command output lines terminated in CR+CR+LF ASCII characters, even the empty lines, so it is necessary to check if the %%b part exists to avoid to process empty lines.
This will do it:
for /f "tokens=2,3 delims=," %%a in ('WMIC CPU Get NumberOfCores^,NumberOfLogicalProcessors /value /format:csv') do set "both=Cores=%%a Processors=%%b"
/format:csv changes up the format some, making it easier for us to manipulate, which I did in the above command.

Get Multiple NICs Speed 10/100/1000

I have the following batch file, however, it freaks out if you have more than one NIC enabled and spits out output of Speed for the first NIC it reports, but for any more, it reports
Speed = Missing Operand
How can I fix this?
#echo off
for /f "tokens=2 delims==" %%a in ('wmic nic where NetEnabled^=true get speed /value ^| find /i "speed"') do set /a speed=%%a
set /a speed=((%speed%/1024)/1024)
echo Speed in megabytes: %speed% Mbps
pause
In addition, why doesn't it work in batch file execution without the '^' before the '=' and '|'
#ECHO OFF &SETLOCAL
for /f "tokens=2 delims==" %%a in ('wmic nic where NetEnabled^=true get speed /value ^| find /i "speed"') do set "speed=%%a"
set /a speed=speed/1048576 2>nul
if %speed% neq 0 (echo Speed in megabytes: %speed% Mbps) else echo No speed available.
You need to escape = and | in the for-loop, no matter if batch or cmd window. It's because it's in the for-loop.

BATCH Alert user to low system memory or disk space

Having tried variations of the following, and with further additional code, what is wrong here? Thanks!
#echo off
goto checkmemorystatus
:checkmemorystatus
for /f "skip=1" %%p in ('wmic os get freephysicalmemory') do (
set m=%%p
if %m% LSS <threshold> (start echo FREE RAM ALERT: %m% & PING 1.1.1.1 -n 1 -w 60000 >NUL)
goto checkmemorystatus
)
no need for delayed expansion (as there is no need for an additional variable):
#echo off
:checkmemorystatus
PING localhost -n 2 -w 60000 >NUL
for /f "skip=1" %%p in ('wmic os get freephysicalmemory') do (
if %%p leq 700000 ( echo FREE RAM ALERT: %%p ) else (echo FREE RAM ok : %%p)
goto checkmemorystatus
)
(the code is basically taken from mihai_mandis)
wmic gives you more lines than you want. You already ignored the first line with skip. The above code breaks the for-loop after one run and to ignore the following lines. ( I added a "Free RAM ok" for testing purposes - you may want to delete it)
At the beginning of the script set delayed expansion:
setlocal EnableDelayedExpansion
This will allow you to access variables in for loops in format !var_name!
2.Do not use goto statements inside for block. This will cause for break.
#echo off
setlocal EnableDelayedExpansion
:checkmemorystatus
for /f "skip=1" %%p in ('wmic os get freephysicalmemory') do (
set mm=%%p
if !mm! LSS <threshold> (
start echo FREE RAM ALERT: !mm!
PING localhost -n 1 -w 60000 >NUL
)
)
goto :checkmemorystatus

Can a wmic processes table (within the command prompt window) be sorted by a value?

I've been exploring various options, such as the /format flag, however it appears that sorting is only possible in XML or HTML output. I would like to sort within command prompt itself. It seems that even TaskList cannot do this (and I would prefer to use wmic since it seems to perform faster).
An example of a command to run would be wmic process get name,processid,workingsetsize. Processes appear to be sorted by processid, but it would make much more sense (in my use case, at least) to sort by name or memory usage (workingsetsize).
As requested, here's an example wmic process table output:
Name ProcessId WorkingSetSize
System Idle Process 0 20480
System 4 765952
smss.exe 384 393216
csrss.exe 500 2850816
wininit.exe 596 774144
csrss.exe 612 6230016
winlogon.exe 672 2023424
services.exe 696 7192576
lsass.exe 704 9814016
svchost.exe 820 5287936
svchost.exe 872 7454720
atiesrxx.exe 936 1028096
Obviously, process lists can get very long, so I've cut it off there.
#ECHO OFF
SETLOCAL
FOR /f "delims==" %%i IN ('set $ 2^>nul') DO SET "%%i="
FOR /f "delims=" %%i IN ('wmic process get Name^, ProcessId^, WorkingSetSize ') DO (
IF DEFINED $0 (
SET wss=%%i
CALL SET wss=0000000000000000000%%wss:~60%%
CALL SET wss=%%wss: =%%
CALL SET wss=$%%wss:~-20%%
CALL SET %%wss%%=%%i
) ELSE (SET $0=%%i)
)
FOR /f "tokens=1*delims==" %%i IN ('set $') DO ECHO(%%j
This should do your sort.
The sticking points are: the WMIC command requires commas between fieldnames. These need to be escaped in the for/f
All environment variables starting "$" are first deleted, then each line of WMIC is processed. The first line (the header) is saved in $0 then each line is saved in $size
The trick here is that SIZE in the listing is not only left-justified by space-padded, hence the string of zeroes is first prefixed to the contents of the workingsetsize column which probably actually begins in column 62 - but column 61 is a space. "60" is used since the substring facility counts from column 0, not column 1.
Then each space is replaced by [nothing], stripping out the trailing spaces (also conveniently, the space from col 61)
The last 20 characters from the resultant string form a leading-zero-filled version of the workingsetsize column.
prepend a $ to tha, and set the resultant variable to the contents of the line that generated it
Finally, listing the contents of the $ variables produces the required list in the required order.
Note that the claims that the WMIC process output described is in order whatever is erroneous. Closer examination would reveal that it is not in order of ProcessID - either alphabetically (since that column is also left-justified) or alphabetically.
OK - revised version, should auto-adjust to width of process-name column:
#ECHO OFF
SETLOCAL
FOR /f "delims==" %%i IN ('set $ 2^>nul') DO SET "%%i="
SET /A COUNT=0
FOR /f "delims=" %%i IN ('wmic process get Name^, ProcessId^, WorkingSetSize ') DO (
IF DEFINED $0 (
SET wss=%%i
CALL %%lopcmd%%
CALL SET wss2=%%wss2: =%%
SET /A COUNT+=1
CALL SET wss=$%%wss2:~-20%%%%COUNT%%
CALL SET %%wss%%=%%i
) ELSE (SET $0=%%i&set/a wsscol=0&CALL :findcol&SET $0=%%i)
)
FOR /f "tokens=1*delims==" %%i IN ('set $') DO ECHO(%%j
GOTO :eof
:findcol
IF "%$0:~0,1%"=="W" SET lopcmd=CALL SET wss2=0000000000000000000%%wss:~%wsscol%%%&GOTO :eof
SET $0=%$0:~1%
SET /a wsscol+=1
GOTO findcol
No substantial changes - just calculate the width required by locating the "W" in the header and establishing an appropriate command to set the variable ready for processing...and which has to executed using the CALL %%var%% method...
Minor edit: introducing COUNT to distinguish between lines with identical workingsetsizes. Count is simply extends the variable-name used for sorting and makes the name unique.
Is this any use to you? It works in Win 8 to provide a list of processes sorted by memory usage.
tasklist /nh |sort /+65
This might work on the command line (depends on Windows version, without any warranty!):
(for /f "delims=" %i in ('wmic process get Name^, ProcessId^, WorkingSetSize ') do #set "wss=%i" &call set "wss=%wss:~0,35%%wss:~-12%"&call echo(%wss%) | more +1 | sort /+35
I understand your question and this works just fine for me on Windows 10:
wmic process list get name, version |sort
or
wmic process list status |sort
or
wmic process list io |sort
Expounding upon Foxidrive's answer, this works with tasklist also:
tasklist /nh | sort
and no need for the /+65 (thanks for that info foxidrive)
"tasklist" on Windows 10 will do this also:
tasklist /nh /fo table
or
tasklist /fo list
or
tasklist /nh |sort
I used Peter's technique here but returned the data in CSV format, which is easier to parse for a batch file in "PID, WorkingSetSize, Name" format sorted on the size column.
#ECHO OFF
SETLOCAL
FOR /f "delims==" %%i IN ('set $ 2^>nul') DO SET "%%i="
FOR /f "skip=2 tokens=2-4 delims=," %%a IN ('cmd /c "wmic process get Name, ProcessId, WorkingSetSize /format:csv" ') DO (
SET wss=0000000000000000000%%c
CALL SET $%%wss:~-20%%=%%b,%%c,%%a
)
FOR /f "tokens=1*delims==" %%i IN ('set $') DO ECHO(%%j
pause
If PowerShell is an option for you then the following should work:
PS C:\> Get-CimInstance -query "select * from win32_process" | Sort-Object -Property Name | Format-Table -Property Name, ProcessId, WorkingSetSize

Using set for numeric value (batch)

I am attempting to write a batch program that will monitor cpu usage and stop a virus scan if cpu usage is high. It will then restart the scan when cpu usage drops.
ECHO Checks if the total CPU usage is greater than 10%
SET scanEnd=0
tasklist /FI "IMAGENAME eq scan32.exe" 2>NUL | find /I /N "scan32.exe">NUL
IF "%ERRORLEVEL%"=="0" (
ECHO Program is running
wmic cpu get loadpercentage /value
FOR /f "tokens=2-3 delims==" %%b in ('wmic cpu get loadpercentage /value') do (
echo %%b >> tempfile.txt
echo removed %%a)
SET /a load < tempfile.txt
DEL tempfile.txt
ECHO Load is "%load%"
IF load GEQ 10 (
ECHO High cpu usage
TSKILL scan32
SET scanEnd=1
))
PAUSE
IF "1" == "%scanEnd%" (
ECHO Scan not finished
IF load LSS 10 (
ECHO Restarting scan
"C:\Program Files\McAfee\VirusScan Enterprise\scan32.exe"
SET scanEnd=0))
ECHO End of program
PAUSE
wmic returns the cpu usage in the form LoadPercentage=0 (or other number). I filter this with the for loop and assign the digit to load. For reasons I do not understand, there is something wrong with the assignment. I am unable to echo the value (displays "") and no matter how I define high cpu usage, load passes the IF GEQ statement. Even a 0% load is apparently greater than 10. I know the problem is with set because I checked the tempfile.txt and it is filtered correctly, but I still have no idea why it's wrong.
Thanks for any help.
you assumed that SET command can read from stdin which is not the case.
You might simply assign the FOR variable into a new variable.
Try this
for /f "tokens=2-3 delims==" %%a in ('wmic cpu get loadpercentage /value') do (
set /a load=%%a
)
and then
if %load% geq 10 (
echo load greater than 10%
)
but beware of the assignments inside FOR loops. You may need to enable delayed expansion for them to work correctly, in case there are more than one assignment in the loop. Eventhough this is not your case, you'd just need to adjust
setlocal enabledelayedexpansion
and then refer to it using this optional syntax
if !load! geq 10 (

Resources