1

I have a script that searches for a running process (notepad) and it will stay runnning until notpad is no longer running:

:search
TASKLIST|FIND "notepad.exe" >nul 2>&1
IF %ERRORLEVEL% equ 0 (GOTO found)
timeout 3 > nul
GOTO search

:found
exit

What i'd like to insert here is a prompt for keypress to exit this loop and jump to :foundIf no keypress is entered the file keep looking for notepad until it closes.

Kalamalka Kid
  • 240
  • 4
  • 14
  • 2
    Type `waitfor /?` Instead of `timeout` use `waifor /t 3 cat`. When you kill it send in another batchfile `waitfor /si cat`. –  Jun 03 '20 at 06:48
  • @Mark what is cat? – Kalamalka Kid Jun 03 '20 at 07:20
  • A signal. I had to think of a word. I choose cat. Make it dog. In help they use `CopyDone` as an example.. If you create a shortcut to the second batch you can assign a hotkey in Properties. –  Jun 03 '20 at 07:44
  • thanks but I am not looking to make a second batch file. I simply want to be able to interrupt the script above with a keystroke if that is possible. – Kalamalka Kid Jun 03 '20 at 08:09
  • Ctrl+C interrupts batchfiles. –  Jun 03 '20 at 08:52
  • 2
    See duplicate question [here](https://stackoverflow.com/a/61754467/12343998) – T3RR0R Jun 03 '20 at 09:46
  • 1
    Nice idea to use `waitfor`, @Mark! you don't even have to use a second batch file, you can let a batch file call itself in a new `cmd` instance (similar to the answer user @T3RR0R linked to), so one instance waits and the other one sends the signal… – aschipfl Jun 03 '20 at 11:32

3 Answers3

2

I corrected your logic a bit (to match stay running until notepad is no longer running)

:search
TASKLIST|FIND "notepad.exe" >nul 2>&1
IF errorlevel 1 GOTO :cont
choice /c cn  /t 3 /d n /n /m "[C]ancel"
if errorlevel 2 goto :search
echo you cancelled.
goto :eof

:cont
echo Notepad has closed.
exit /b

For an explanation of the choice command see choice /?
For an explanation of the changed if errorlevel syntax, see if /?

or - if you don't like the continuous [C]ancel - suppress the output of choice and print the hint before the loop:

echo [C]ancel
:search
TASKLIST|FIND "notepad.exe" >nul 2>&1
IF errorlevel 1 GOTO :cont
choice /c cn  /t 3 /d n >nul
if errorlevel 2 goto :search
echo you cancelled.
goto :eof

:cont
echo Notepad has closed.
exit /b
Stephan
  • 53,940
  • 10
  • 58
  • 91
  • works perfectly. Wondering how I might replace C to cancel with ENTER? – Kalamalka Kid Jun 03 '20 at 15:34
  • 1
    nope. `choice` supports [a-z], [A-Z], [0-9] and ASCII between 128 and 254. If you really need "ENTER" (or "AnyKey") there is an ugly `xcopy` hack (not recommended unless absolutely necessary. You can extract it from [here](https://www.dostips.com/forum/viewtopic.php?t=4741)) – Stephan Jun 03 '20 at 15:46
  • ok thanks. Is there another way instead of using `choice` to jump out of a loop using `ANYKEY` or `ENTER`? Is this worthy of a new question? – Kalamalka Kid Jun 03 '20 at 15:51
  • If you find a promising way and just can't quite make it work, yes, ask another question. If not, it's just a code request and off-topic for this site. Did you take a look at the possible duplicate, @T3RR0R mentioned? – Stephan Jun 03 '20 at 16:05
  • This solution is useful, but you could do away with a chunk of it by using the errorlevel with default timeout switches for the choice command with labels :0 and :1 - so long as you don't mind passing up on the cancelled / closed feedback – T3RR0R Aug 27 '20 at 17:31
1

The timeout command waits for the specified time and can be terminated by a key-ress, given that the option /NOBREAK is not specified. It outputs a text like Waiting for 3 seconds, press a key to continue ... and updates the seconds value each second by placing the cursor right to the number, then writing out as many backspace characters as needed and then overwriting the number. This whole text, including the backspaces, can be captured by a for /F loop and then checked for the last number being 0; if it is not, the user pressed a key, aborting the timeout command.

This is how this approach could be coded:

@echo off
rem // Retrieve the backspace character:
for /F %%B in ('prompt $H ^& for %%B in ^(.^) do rem/') do set "_BS=%%B"

:SEARCH
rem // Improved filtering for the process:
tasklist /FI "ImageName eq notepad.exe" | find "=" > nul
if not ErrorLevel 1 goto :FOUND
rem /* Use `timeout` without `/NOBREAK` option to allow abortion of the waiting;
rem    `timeout` counts down the seconds and precedes each number with backspace
rem    characters, and the whole output string is captured by `for /F`; when no
rem    key is pressed, then seconds reach `0`, otherwise not: */
for /F "delims=" %%T in ('timeout /T 3') do set "TOUT=%%T"
rem /* Now let us replace each backspace by `" "` and surround the whole string
rem    with `""`, so we get space-separated quoted strings:
call set TOUT="%%TOUT:%_BS%=" "%%"
rem // Loop through those strings and assign each to the same variable:
for %%T in (%TOUT%) do set "TOUT=%%~T"
rem // The last string is now checked against `0`; if it is, no key was pressed:
if "%TOUT%"=="0" goto :SEARCH
rem // This point is only reached if a key-press aborted the `timeout` command.

:FOUND
exit /B
aschipfl
  • 33,626
  • 12
  • 54
  • 99
0

Since Windows 7, there is the waitfor command (thanks to this comment!) to send or to wait for a signal, optionally supporting a timeout, upon which the ErrorLevel becomes set; ErrorLevel is cleared when a signal is received. This could be used when there is another parallel process (initiated by the start command to not wait until finished) that detects a key-press and sends such a signel then.

Here is how that approach could be coded:

@echo off
rem // Jump to label if first argument is such:
set "ARG1=%~1 "
if "%ARG1:~,1%"==":" goto %~1
rem // Define name of signal and related console window title:
set "_SIGNAL=KeyPress%RANDOM%"
set "_WTITLE=///%_SIGNAL%///"
rem // Actually run key-press routine in a new instance of `cmd.exe`:
start "%_WTITLE%" /B cmd /D /C call "%~f0" :SEND

:SEARCH
rem // Improved filtering for the process:
tasklist /FI "ImageName eq notepad.exe" | find "=" > nul
if not ErrorLevel 1 goto :FOUND
rem // Await signal at this point, establishing a timeout:
waitfor /T 3 %_SIGNAL% > nul 2>&1
rem // `waitfor` sets the `ErrorLevel` in case no signal was received:
If ErrorLevel 1 goto :SEARCH
rem // This point is only reached if a key-press provoked the signal to be sent.

:FOUND
rem /* Kill key-press routine (other `cmd.exe` instance) if it is still running;
rem    since it is usually in `pause` state, its status might be `Unknown`: */
set "PID=" & for /F "tokens=2 delims=: " %%T in ('
    tasklist /FI "ImageName eq cmd.exe" /FI "WindowTitle eq %_WTITLE%" /FO LIST ^| findstr "^PID:" ^& ^
        tasklist /FI "ImageName eq cmd.exe" /FI "Status eq Unknown" /FO LIST ^| findstr "^PID:"
') do (taskkill /PID %%T || taskkill /PID %%T /F) > nul 2>&1
exit /B

:SEND
pause > nul
waitfor /SI %_SIGNAL% > nul
exit /B
aschipfl
  • 33,626
  • 12
  • 54
  • 99