0

I've been trying to create a file that times the user's input. I could get this file

start /min b1.bat
(:b1.bat)
@echo off
cls
set num=0
:time
set /a num=%num%+1
echo %num% >waiter.txt
set time=0
:wait
cls
set /a time=%time%+1
if "%time%" equ "1000" goto time
goto wait

Here, b1.bat counts the secconds passed since it has started.

(:b2.bat)
cls
:begin
set /p time= <waiter.txt
echo %time%    
if "%time%" equ "5" echo Five secconds.
set /p cho=Input:
goto begin

I would like to know how to make b2.bat do something if no input was given for an amount of time. No input at all, not even a press of enter.

So, if I didn't press any key at all and a minute has passed, it prints A minute has passed without input.

I hope this makes sense to you.

1 Answers1

0

Before I answer your question, I need to point out that looping 1000 times does not necessarily mean you've paused for 1 second. There are better ways to sleep -- timeout /t seconds in Vista or newer, or ping -n seconds+1 localhost or choice /t seconds in XP. You should also avoid stomping on existing environment variables, such as %time%.

To answer your question, there's no graceful way to set a timeout for set /p. You can, however, launch a background thread within the same window using start /b to echo things on an interval. You can set up inter-thread communication between the active thread and the background using waitfor (Vista or newer only) to reset the timer, and set the helper thread to self-destruct when waiter.txt disappears.

And as long as you're launching one helper thread within the same window, might as well put your waiter.txt thread there as well. Why spawn a minimized window when you can reuse the one you already have? And since we're merging process threads within the same window, might as well go one step further and merge all your scripts into one script that re-launches itself with different arguments for different threads.

@echo off
setlocal

if "%~1"=="" (
    >waiter.txt echo 0
    start /b "" "%~f0" helper1
    start /b "" "%~f0" helper2
) else goto %~1

:begin
cls
set /p "seconds="<waiter.txt
echo Alive for %seconds%s.

:read-host
set "cho="
set /p "cho="
if not defined cho goto read-host

rem // delayed expansion prevents evaluation of special characters in user input
setlocal enabledelayedexpansion
for %%c in (quit exit die EOL) do if /i "!cho!"=="%%c" (
    del waiter.txt
    echo OK, bye!
)
rem // send signal to helper2 acknowledging entry
waitfor /s %computername% /si UserEntry >NUL 2>NUL
if not exist waiter.txt exit /b

rem // ========================================
rem // Do something meaningful with !cho! here.
rem // ========================================
endlocal

goto begin

rem // formerly b1.bat
:helper1
set "num=0"
:helper1_loop
timeout /t 1 /nobreak >NUL 2>NUL
set /a num+=1
if not exist waiter.txt exit
>waiter.txt echo %num%
goto helper1_loop

:helper2
if not exist waiter.txt exit
waitfor /t 60 UserEntry >NUL 2>NUL || (
    echo A minute has passed without input.
)
goto helper2

For extra credit, let's get rid of waiter.txt as well. I'm not sure whether this paranoia is founded or not, but I believe that hard drive sectors can endure a finite number of writes. Rewriting waiter.txt once a second for potentially hours bothers me. So let's create a hybrid Jscript chunk to calculate the seconds elapsed between the start of the script and now.

To control execution of the background helper thread, we'll still use a temporary file, but we'll use a lock file that will only be written once, then deleted at the end.

@if (@CodeSection == @Batch) @then

@echo off
setlocal

if "%~1"=="" (
    rem // relaunch self as helper if lock file is writeable
    2>NUL (>"%~dpn0.lock" type NUL) && start /b "" "%~f0" helper

    rem // create a lock file for the duration of the main runtime
    rem // Credit: Dave Benham -- https://stackoverflow.com/a/27756667/1683264
    8>&2 2>NUL (2>&8 9>"%~dpn0.lock" call :init) || (
      echo Only one instance is allowed.
      timeout /t 3 /nobreak >NUL
      exit /b
    )
) else goto %~1

rem // cleanup and end main runtime
del "%~dpn0.lock" >NUL 2>NUL
waitfor /s %computername% /si UserEntry >NUL 2>NUL
exit /b

:init
rem // set %start% to current epoch
call :jscript start

:begin
cls
call :jscript seconds
echo Alive for %seconds%s.

:read-host
set "cho="
set /p "cho="
if not defined cho goto read-host

rem // delayed expansion prevents evaluation of special characters in user input
setlocal enabledelayedexpansion
for %%c in (quit exit die EOL) do if /i "!cho!"=="%%c" (
    echo OK, bye!
    exit /b
)
rem // send signal to helper2 acknowledging entry
waitfor /s %computername% /si UserEntry >NUL 2>NUL

rem // ========================================
rem // Do something meaningful with !cho! here.
rem // ========================================
endlocal

goto begin

:helper
del "%~dpn0.lock" >NUL 2>NUL
if not exist "%~dpn0.lock" exit
waitfor /t 60 UserEntry >NUL 2>NUL || (
    echo A minute has passed without input.
)
goto helper

:jscript
for /f %%I in ('cscript /nologo /e:JScript "%~f0" "%start%"') do set "%~1=%%I"
goto :EOF

@end // end batch / begin JScript hybrid code
WSH.Echo(
    WSH.Arguments(0)
    ? Math.round((new Date() - WSH.Arguments(0)) / 1000)
    : new Date().getTime()
);
Community
  • 1
  • 1
rojo
  • 24,000
  • 5
  • 55
  • 101