Variable not recognized in else statement in a bat file - windows

I have the follow code:
ECHO OFF
:LOOP
set c=
setlocal ENABLEDELAYEDEXPANSION
tasklist | find /i "calc.exe" >nul 2>&1
IF %ERRORLEVEL% == 0 (
ECHO easymeetingOnCall is running
taskkill /IM calc.exe /F
set c=true
Timeout /T 10
goto LOOP
) ELSE (
IF "!c!" == "true" (
ECHO l'applicazione easymeetingOnCall è spenta.
Timeout /T 10
goto exitLoop
)
ECHO easymeetingOnCall is not running
Timeout /T 5 /Nobreak
GOTO LOOP
)
:exitLoop
My problem is that the follow condition
IF "!c!" == "true"
Into the else statement, is not recognized.
If I write echo !c! It doesn't output any result.
I set enabledelayedexpansion before, so I don't know why of this behavior.
Could you help me?
Thanks to all.

Look carefully at the following extraction from your code.
ECHO OFF
:LOOP
set c=
....
set c=true
goto :LOOP
You are explicitly telling the if statement to goto :loop where directly after you set c= which now gives it no value. Move the set c= to above to label to retain the value you've set.
I however suggest a few changes. you can get away without delayedexpansion and do actually require all the for loops. Using taskkill to explicitly search for the app is better than listing all, also already mentioned to you in a comment by #Mofi. Lastly, it is not really good practice to set single character variables, though it does not always cause issues, I would suggest using multple character variable names. I just changed your variable %c% to %_c%
#echo off & set _c=
:loop
tasklist /FI "IMAGENAME eq Calc.exe" | find /i "Calc"
goto :_%errorlevel%
:_0
ECHO easymeetingOnCall is running
taskkill /IM calc.exe /F
set _c=true
Timeout /T 10
goto loop
:_1
IF "%_c%" == "true" (
ECHO l'applicazione easymeetingOnCall è spenta.
Timeout /T 10
goto exitloop
)
ECHO easymeetingOnCall is not running
Timeout /T 5 /Nobreak
goto loop
:exitloop
Edit, as you wanted to do this with a list:
#echo off & set _c=
set "list=calc.exe StickyNot.exe wordpad.exe"
for %%a in (%list%) do call :loop %%a
goto :eof
:loop
set task=%1
tasklist /FI "IMAGENAME eq %1" | find /i "%1"
if not errorlevel 1 (
ECHO easymeetingOnCall is running %1
taskkill /IM %1 /F
set _c=true
Timeout /T 10
)
if "%_c%" == "true" (
ECHO l'applicazione easymeetingOnCall è spenta. %1
Timeout /T 10
goto exitloop
)
echo easymeetingOnCall is not running %1
Timeout /T 5 /Nobreak
goto :loop
:exitloop
Be aware, if the first process is not running, it will loop forever until it is found, then only kill it and go to the next process. If you do not want that, then change goto :loop in the last line to exit loop.

Related

Batch file to determine websites in IE

Is there a piece of code which can be used in a batch file to determine if firstly IE is open, and if so which websites are open in the tab(s)?
I am needing to force close IE when a user locks their PC, and if a specific website is open.
Thanks in advance.
Give a try for this batch file :
#echo off
Title Checking Running Process and Get their command lines
setlocal enabledelayedexpansion
Mode 90,25 & color 0A
Set "ProcessName=iexplore.exe"
Set "String2Search=facebook"
Call :CheckRunning !ProcessName!
If /I "%flag%"=="True" (
color 0A
echo %flag%
Call :GetCommandLine !ProcessName!
echo(
Call :CheckString_in_URL "%String2Search%"
) else (
color 0C
echo %flag%
echo !ProcessName! is not running !
pause>nul & exit
)
Exit
::********************************************************************************************************
:CheckRunning <ProcessName>
Set "ProcessName=%1"
tasklist /NH /FI "imagename eq %ProcessName%" 2>nul |find /i "%ProcessName%" >nul
If not errorlevel 1 ( Set "flag=True" ) else ( Set "flag=False" )
Exit /b
::********************************************************************************************************
:GetCommandLine <ProcessName>
Set "ProcessCmd="
for /f "tokens=2 delims==" %%P in ('wmic process where caption^="%~1" get commandline /format:value ^| findstr /I "%~1" ^| find /I /V "%~nx0" 2^>nul') do (
Set "ProcessCmd=%%P"
echo !ProcessCmd!
)
Exit /b
::******************************************************************************************************
:Kill <ProcessName>
Taskkill /IM "%~1" /F>nul 2>&1
Exit /b
::******************************************************************************************************
:CheckString_in_URL <String2Search>
set "String2Search=%~1"
(
echo Set objFso = CreateObject^("Scripting.FileSystemObject"^)
echo Set colShWindows = CreateObject^("shell.application"^).Windows
echo For Each w In colShWindows
echo If objFso.GetFileName^(LCase^(w.FullName^)^) = "iexplore.exe" Then
echo WScript.Echo w.LocationURL
echo End If
echo Next
)>"%Temp%\iexplore_tabs.vbs"
setlocal EnableDelayedExpansion
for /f %%a in ('cscript //nologo "%Temp%\iexplore_tabs.vbs"') do (
set /a index+=1
set "URL[!index!]=%%a"
)
for /L %%i in (1,1,%index%) do (
echo !URL[%%i]! | find /I "%String2Search%">nul && (
echo Found this string "%String2Search%" in the URL "!URL[%%i]!"
echo(
echo Did you want to kill this process "!ProcessName!" Y/N ?
Set /p "Answer="
If /I "!Answer!"=="Y" ( Call :Kill "!ProcessName!" ) else ( exit )
) || (
echo No string like "%String2Search%" found in "!URL[%%i]!"
Timeout /T 5 /nobreak>nul
)
)
exit /b
::******************************************************************************************************
I'm grateful to aGerman that give me the idea of how to enumerate the URLs of the InternetExplorer tabs

Batch compare timer result to a number

I have a batch timer (saw it in a different stackOverFlow post) and I need to compare the elapsed time to a number in order to know when 10 seconds are pass, however, I'm getting an error when trying to compare the two.
What am I doing wrong?
#set /A _tic=%time:~0,2%*3600^
+%time:~3,1%*10*60^
+%time:~4,1%*60^
+%time:~6,1%*10^
+%time:~7,1% >nul
echo RUN-KILL-WATCHDOG-THE-SMART-WAY
SETLOCAL EnableExtensions
set EXE=python.exe
:RUNNERON
FOR /F "skip=1" %%x IN ('wmic process where "CommandLine Like '%%forking import main%%' and Caption='python.exe'" get caption') DO for /F "delims=" %%j in ("%%x") do (
echo The name of process who passed filter is: %%j Waiting for it to terminate...
IF %%j == %EXE% (
:: TOK
set /A _toc=%time:~0,2%*3600^
+%time:~3,1%*10*60^
+%time:~4,1%*60^
+%time:~6,1%*10^
+%time:~7,1% >nul
set /A _elapsed=%_toc%-%_tic
:: echo %_elapsed% seconds.
IF %_elapsed%==10 goto TIMEOUT :: EVERYTHING WORKS BUT THIS LINE
goto RUNNERON
) ELSE (
echo Entered "else" part - something went wrong.
)
)
echo Runner is done - killing watchdog process
taskkill /F /im "python.exe"
: TIMEOUT
echo TIMEOUT
pause
when I run it I get the error:
"goto was unexpected at this time."
What am I missing?
Thank you

Manipulate cmd ping color based on time

My internet is not always working properly and I'd like to check the quality based on the cmd windows tool. I believe it's a task simple enough for it to handle.
I've begun by making a shortcut so I can have easy access to the command:
C:\Windows\System32\PING.EXE 8.8.8.8 -t
Now I was trying to transform the cmd ping command into a visually responsive one based on the output. I'd like to make the color change according to the time response.
After looking and not finding anything related, I believe it's either impossible or no one has ever tried.
Thank you very much :)
PD: (In case there was anything unclear just ask and I'll gladly answer)
Based on Magoo's post, I wrote this little batch program.
It asks for the target, the number of requests to make, the max time allowed and the time between requests and then prints in red if the request is over the time max, otherwise it sums the number of requests. It includes timestamp to be more accurate.
Copy and paste in a text file and name it with extension ".bat" (But don't name it "ping.bat" otherwise the program will enter in an infinite loop).
REM CMD PING TOOL
REM By Daweb
REM https://stackoverflow.com/users/3779294/daweb
#ECHO OFF
REM Needed for Line colored
SETLOCAL EnableDelayedExpansion
FOR /F "tokens=1,2 delims=#" %%a IN ('"PROMPT #$H#$E# & echo on & for %%b in (1) do rem"') do (
SET "DEL=%%a"
)
for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a"
ECHO *****************
ECHO * CMD PING TOOL *
ECHO *****************
REM Start
:start
ECHO.
ECHO Set yours values
REM SET Target
SET /p hostInput=" - Target (ip or hostname): "
If "%hostInput%"=="" ECHO.&GOTO start
REM SET loops
SET /p loopsInput=" - Requests number: "
SET /a loops=loopsInput
REM SET time limit
SET /p maxmsInput=" - Maximum Time Limit (ms): "
SET /a maxms=maxmsInput
REM Value used for sleep between loops
SET /p sleepInput=" - Delay between requests (s): "
SET /a sleepDelay=sleepInput+1
REM Variables
SET displayText=""
SET /a countRequestsOk=0
SET /a countRequestsKo=0
SET /a totalRequests=0
SET /a maxTime=0
ECHO.
ECHO START at %TIME% [target: %hostInput%, requests: %loops%, time limit: %maxms% ms, delay: %sleepInput% s]
ECHO.
REM Loop
:loop
REM Set time
FOR /f "tokens=1-3 delims=/:" %%a IN ("%TIME%") DO (SET mytime=%%ah%%bm%%cs)
REM Get ping value
FOR /f "tokens=3delims==" %%a IN ('PING -n 1 %hostInput%') DO FOR /f "delims=m" %%b IN ("%%a") DO (
SET /a timems=%%b
SET /a totalRequests+=1
REM Check result
IF !timems! GTR %maxms% ( GOTO failed ) ELSE ( GOTO success )
)
REM Request success
:success
SET /a countRequestsOk+=1
IF !timems! GTR !maxTime! ( SET /a maxTime=timems )
<nul set /P "=!countRequestsOk! requests [Max !maxTime! ms]!CR!"
GOTO next
REM Request failed
:failed
IF !countRequestsOk! GTR 0 ECHO.
SET /a countRequestsOk=0
SET /a countRequestsKo+=1
SET displayText=" %mytime% - !timems!ms"
CALL :ColorText 0c !displayText!
GOTO next
REM Next loop
:next
REM Sleep a little bit
IF %sleepDelay% GTR 1 ( ping -n %sleepDelay% localhost > nul )
REM Check continue
SET /a loops-=1
IF %loops% gtr 0 GOTO loop
REM Display result
IF !countRequestsOk! GTR 0 ECHO.
ECHO.
ECHO STOP at %TIME%
ECHO.
if !countRequestsKo! GTR 0 (
SET displayText="FAILED - !countRequestsKo! requests over %maxms% ms on !totalRequests! requests in total"
CALL :ColorText 0c !displayText!
) ELSE (
SET displayText="SUCCESS - No request over %maxms% ms on !totalRequests! requests in total"
CALL :ColorText 02 !displayText!
)
REM Ask if restart
ECHO.&ECHO *********************
SET /p restartInput="Do it again ? (Y/N): "
If "%restartInput%"=="" ECHO *********************&GOTO start
If /I "%restartInput%"=="y" ECHO *********************&GOTO start
If /I "%restartInput%"=="n" ECHO *********************&GOTO end
REM End
:end
PAUSE
GOTO :EOF
REM Line color
:ColorText
ECHO off
ECHO %DEL% > "%~2"
FINDSTR /v /a:%1 /R "^$" "%~2" NUL
DEL "%~2" > NUL 2>&1
#ECHO OFF
SETLOCAL
SET loops=10
:loop
FOR /f "tokens=3delims==" %%a IN ('PING 8.8.8.8 -n 1') DO FOR /f "delims=m" %%b IN ("%%a") DO ECHO %%b&COLOR %%b&GOTO cchgd
:cchgd
PAUSE
SET /a loops-=1
IF %loops% gtr 0 GOTO loop
COLOR
GOTO :EOF
A simple demonstration - repeats the ping 10 times, changing colours depending on the response. Manipulate to do as you wish...
I am not sure that I know what the desired output should be, but this will output GREEN text for response times 0-39 ms, YELLOW for 40-79 ms, and RED for 80+ ms.
Run this from a cmd.exe prompt using the following command or put it into a .bat file script. Change the directory to the location where the Get-PingColor.ps1 file is landed.
powershell -NoLogo -NoProfile -File "%USERPROFILE%\bin\Get-PingColor.ps1"
=== Get-PingColor.ps1
[CmdletBinding()]
param (
[Parameter(Mandatory=$true)]
[string[]]$ComputerNames
,[Parameter(Mandatory=$false)]
[int]$Count = 4
,[Parameter(Mandatory=$false)]
[int]$SpeedMinimumSlow = 80
,[Parameter(Mandatory=$false)]
[int]$SpeedMinimumMedium = 40
)
foreach ($ComputerName in $ComputerNames) {
$Pings = Test-Connection -ComputerName $ComputerName -Count $Count
$Average = ($Pings | Measure-Object -Property responsetime -Average).Average
$ForegroundColor = 'Green'
if ($Average -ge $SpeedMinimumSlow) { $ForegroundColor = 'Red'}
else { if ($Average -ge $SpeedMinimumMedium) { $ForegroundColor = 'Yellow' }}
Write-Host -ForegroundColor $ForegroundColor -BackgroundColor 'Black' "$ComputerName $Average ms"
}
=== Execution examples
I am loathe to put images into a post, but I do not see a way to produce color on SO.

IF/Else and Goto issues

I've been struggling with trying to put together this batch script to check a process (it can be any process). count the number of instances of it are running and save the output of that count. If its not running (a count of 0) then its fine but if it is running (1 or greater) but is under say '5' in this case have it echo a statement.
every time i try to do this i either get 'else' is not recognized as an internal or external command, operable program or batch file or when the script gets to the goto :problem it seems to completely ignore the else statement.
#echo off
for /f %%g in (' tasklist /FI "IMAGENAME eq chrome.exe" ^| find /I /C "chrome.exe" ') do (
if %%g EQU 0 goto :notrunning
if %%g GTR 0 goto :problem
)
:notrunning
echo Program is not running
pause
:problem
If %%g LSS 5 ( echo problem
) else ( echo chrome is fine
)
pause
Any goto breaks the for context. Try this:
#echo off
for /f %%g in (' tasklist /FI "IMAGENAME eq chrome.exe" ^| find /I /C "chrome.exe" ') do set status=%%g
if %status% EQU 0 goto :notrunning
if %status% GTR 0 goto :problem
:notrunning
echo Program is not running
pause
:problem
If %status% LSS 5 ( echo problem
) else ( echo chrome is fine
)
pause
Try to avoid the else statement in the if command. It may not be supported in the version of windows you are using. Try to get rid of the else statement and have multiple if statements. Here, try this:
#echo off
for /f %%g in (' tasklist /FI "IMAGENAME eq chrome.exe" ^| find /I /C "chrome.exe" ') do (
if %%g EQU 0 goto :notrunning
if %%g GTR 0 goto :problem
)
:notrunning
echo Program is not running
pause
:problem
If %%g LSS 5 echo problem
If %%g GTR 4 echo chrome is fine
)
pause
%%g is out-of-scope when you reach the label :problem
you need
if %%g GTR 0 set /a somevar=%%g&goto :problem
...
:problem
If %somevar% LSS 5 ( echo problem
) else ( echo chrome is fine
)

Return variable through start command

I am looking for a way to get a value return from a start-command launched batch script. Let me explain:
I need to take advantage of multiprocessing by launching multiple sub-batch scripts simultaneously from a main batch script, then retrieve every sub batch file return value when they're done.
I've been using return variables with the call-command as very well explained by dbenham.
That solution does not allow multithreading, since sub-batch scripts are run one after the other.
Using the start-command allows multiple running batch scripts, but values are not returned to my main script because apparently the start-command creates a whole new variable context.
Does anybody have a solution/workaround to return values from the sub-scripts to the main script ?
Below is a model of what I need:
mainScript.bat
#echo off
setlocal
set "retval1=0"
set "retval2=0"
REM run two scripts in parallel:
start "" subscript1.bat arg1 retval1
start "" subscript2.bat arg1 retval2
REM wait for returned value
:waiting
call :sleep 1
set /a "DONE=1"
if %retval1% equ 0 set "DONE=0"
if %retval2% equ 0 set "DONE=0"
if %DONE% equ 0 goto :waiting
echo returned values are %retval1% %retval2%
exit /b
subscript1.bat
#echo off
setlocal
set "arg1=%~1"
set "retval1=%~1"
REM do some stuff...
REM return value
(endlocal
set "%retval1%=%foo%"
)
exit /b
Can't see any alternative to writing your return values to files, so
main
#ECHO OFF
SETLOCAL
for %%a in (1 2) do (
del "%temp%\retval%%a" 2>nul
)
start /min "" q225220791.bat arg1 retval1
choice /t 1 /d y >nul
start /min "" q225220791.bat arg1 retval2
:waiting
choice /t 1 /d y >nul
ECHO wait...%retval1%...%retval2%
if not exist "%temp%\retval1" goto waiting
if not exist "%temp%\retval2" goto waiting
for %%a in (1 2) do (
for /f "usebackqdelims=" %%i in ("%temp%\retval%%a") do set "retval%%a=%%i"
)
for %%a in (1 2) do (
del "%temp%\retval%%a" 2>nul
)
echo returned values are %retval1% %retval2%
GOTO :EOF
q225220791.bat
#ECHO OFF
SETLOCAL
:: wait a random time 2..10 sec.
SET /a timeout=%RANDOM% %% 8 + 2
choice /t %timeout% /d y >nul
:: return a random result 12..20
SET /a foo=%RANDOM% %% 8 + 12
>"%temp%\%2" echo %foo%
ENDLOCAL
exit
Relying on the value of the second parameter to the sub-process to set the tempfile name. I've changed the names of the batches to suit my system.
Not sure if this is even practical, but just testing to avoid the temporary file. So looking for a place in child process that can be readed from parent process i decided to use the window title.
task.cmd
#echo off
setlocal
rem Retrieve task information and set window title
set "taskID=%~1"
title [task];%taskID%;working;
rem Retrieve the rest of parameters. For this sample, a random value
set /a "timeToWait=%~2 %% 30"
rem Simulate command processing
timeout /t %timeToWait%
rem Calculate a return value for this task
for /f "tokens=1-10 delims=,.:/ " %%a in ("%date%%time%_%~2") do set "returnValue=%%a%%b%%c%%d%%e%%f%%g%%h%%i%%j"
rem Signal the end of the task
title [task];%taskID%;ended; my return value is %returnValue% ;
rem Wait for master to end this tasks
cls
echo Waiting for master....
waitfor %taskID%
rem Cleanup
endlocal
master.cmd
#echo off
setlocal enableextensions enabledelayedexpansion
rem Configure tasks
set "taskPrefix=myTSK"
set "numTasks=5"
rem Start tasks
for /l %%a in (1 1 %numTasks%) do (
set "return[%taskPrefix%%%a]=unknown"
start "[task];%taskPrefix%%%a;working;" cmd /c "task.cmd %taskPrefix%%%a !random!"
)
rem Wait for tasks to start
timeout /t 2 > nul
rem Wait for tasks to end. Get the list of cmd.exe windows with window title
rem to see the state of the task
rem Tasks in working state indicate master needs to keep working
rem Tasks in ended state have the return value in the window title and are
rem waiting for the master to retrieve the value and end them
:wait
set "keepWaiting="
for /f "tokens=9 delims=," %%a in ('tasklist /fi "imagename eq cmd.exe" /fo csv /v ^| findstr /l /c:"[task];%taskPrefix%"'
) do for /f "tokens=2-4 delims=;" %%b in ("%%a") do (
if "%%c"=="working" (
set "keepWaiting=1"
) else if "%%c"=="ended" (
set "return[%%b]=%%d"
start "" /min waitfor.exe /si %%b
)
)
rem If any task has been found in working state, keep waiting
if defined keepWaiting (
echo %time% : waiting ...
timeout /t 5 > nul
goto wait
)
rem All tasks have ended. Show return values
for /l %%a in (1 1 %numTasks%) do (
echo task #%%a ended with exit value :[!return[%taskPrefix%%%a]!]
)
endlocal
In this sample, the task waits for master using waitfor command. In XP this is not available, and can be replaced with just a pause, and from master.cmd the waiting loop must be modified to include the processID token in the tasklist processing, so the waiting task can be closed with taskkill.

Resources