Detect echo state without temporary file

Discussion forum for all Windows batch related topics.

Moderator: DosItHelp

Post Reply
Message
Author
jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Detect echo state without temporary file

#1 Post by jeb » 24 Jan 2018 09:55

Until now it wasn't possible to detect the echo state without a temporary file.

The main problem is, that fetching the current state doesn't work neither with FOR nor with pipes as both invoke a new cmd.exe instance where the echo state is resetted.

The current working solution is to redirect the output of phase3 and test if there was any output, then phase3 was active and echo must be ON.

Code: Select all

@(
    (
        FOR /F %%L in ("1") do REM
    ) > "%TEMP%\echoCheck.tmp"

    FOR /F "delims=" %%F in ("%TEMP%\echoCheck.tmp") do @if %%~zF == 0 (set "EchoState=OFF" ) ELSE (set "EchoState=ON")
    del "%TEMP%\echoCheck.tmp"
    echo EchoState !EchoState!
)
At KEYS as boolean variable within brackets and without delayed expansion I tried something like

Code: Select all

@(
  for %%A in (1) do REM
) 1>&0
That produces error outputs when ECHO is ON, but I can't figure out anything to detect if an error occours at all.
I could redirect the stderr to a file, but then it's not better than the originial solution.

Now, I combined the cursor back technic from Aacini with the phase3 output and it works.

Code: Select all

@echo off
setlocal EnableDelayedExpansion
cls

call :createChar 0x09 TAB
call :createChar 0x08 BS

REM Test1
echo off
@call :detectEchoState
@set echoState_test1=!echoState!
@echo off

REM Test2
@echo on
@call :detectEchoState
@set echoState_test2=!echoState!
@echo off

cls
echo EchoState_test1 !EchoState_test1!
echo EchoState_test2 !EchoState_test2!
exit /b

:detectEchoState
    @cls
    @set "echoState=ON"
    REM
    @for /F "delims=" %%L IN ('"((echo %%TAB%%%%BS%%%%BS%%) > con)  2>&1"') DO @set "echoState=OFF"

@exit /b

:createChar
(set LF=^
%=EMPTY=%
)
for /f eol^=^%LF%%LF%^ delims^= %%X in ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(%~1"') do set "%~2=%%X"
exit /b
The core idea is first to move the curor to the home postion (currently simply by cls).
Then test if phase3 is active with a simple REM.
In the next step I try to go up one line with the TAB-BS-BS trick and redirect all errors to stdout.
The for loop will only get anything when an error occoured and that only happens when the cursor still was still at the home position.
That's all, obviously :D

This solution has still some draw backs.
  1. For the echo state detection the cursor has to been moved to the home position
  2. There is some output to the screen when "ECHO is ON", while testing the state
  3. This solution can't be used for detecting KEYS or VERIFY state, it only works for ECHO

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Detect echo state without temporary file

#2 Post by dbenham » 24 Jan 2018 22:13

Very interesting and ingenious, but I think a temp file is both more practical and probably faster.


I have a variation of your idea that I think can be used to detect KEYS and VERIFY states, but it is language dependent, and may be impossible for some languages.

In English, the length of the output for KEYS (or VERIFY) differs by one because the two possible states are ON or OFF.

So you could use your technique to save the ECHO state, then @ECHO OFF & CLS to get to the home position.

Now use <NUL SET /P "...." & VERIFY (not sure about the number of needed dots) so that some combination of <TAB><BS><BS> will generate an error if ON, but not generate an error if OFF.

This technique could not be used for any language where the length of the state string is the same for ON and OFF.

Edit - Never mind, that can't work
My stupid idea could only work if VERIFY and KEYS did not issue a new line, which of course is not true :(


Dave Benham

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Detect echo state without temporary file

#3 Post by jeb » 25 Jan 2018 02:57

dbenham wrote:Very interesting and ingenious, but I think a temp file is both more practical and probably faster.
Yes, currently it's only a proof of concept, perhaps it can be improved with other ideas.

Your idea is also very good, how to test VERIFY and KEYS, and obviously :D it still can work.

You only have to prefix the output, so only the longer output will wrap to the next line.
Then you got your cursor at the second or third line, dependent of KEYS ON/OFF.

Code: Select all

@echo off
setlocal EnableDelayedExpansion
set /a outputLenLong=29 %= This is the maximal text output size of the command (language dependend), in this case KEYS OFF  =%
mode con cols=80 line=50
cls

call :createChar 0x09 TAB
call :createChar 0x08 BS

REM Test1
keys off
call :detectKeysState
set state_test1=!keysState!

REM Test2
keys on
call :detectKeysState
set state_test2=!keysState!

cls
for /L %%n in (1 1 2) DO (
    echo State_test%%n !state_test%%n!
)
exit /b

:detectKeysState
cls
set "keysState=OFF"
set "prefix=."
set "bigBS=!bs!"
FOR /L %%n in (1 1 7) DO (
    set "prefix=!prefix!!prefix!"    
    set "bigBS=!bigBS!!bigBS!"
)
set "bigBS=!bigBS:~0,12!"
set /a prefixLen=80-outputLenLong
<nul set /p ".=!prefix:~0,%prefixLen%!"
keys
for /F "delims=" %%L IN ('"((echo %%TAB%%%%bigBS%%x) > con)  2>&1"') DO @set "keysState=ON"
exit /b

:createChar
(set LF=^
%=EMPTY=%
)
for /f eol^=^%LF%%LF%^ delims^= %%X in ('forfiles /p "%~dp0." /m "%~nx0" /c "cmd /c echo(%~1"') do set "%~2=%%X"
exit /b
Btw. The mode con cols=80 lines=50 isn't necessary, the screen width (and height) could also be detected with the <TAB><BS> technic.

jeb

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Detect echo state without temporary file

#4 Post by dbenham » 25 Jan 2018 06:45

jeb wrote:Your idea is also very good, how to test VERIFY and KEYS, and obviously :D it still can work.

You only have to prefix the output, so only the longer output will wrap to the next line.
Then you got your cursor at the second or third line, dependent of KEYS ON/OFF.
Yes, I thought of that when I woke up this morning. I'll blame my failure to figure that out last night on my lack of sleep (3 hours) the night before :|
jeb wrote:Btw. The mode con cols=80 lines=50 isn't necessary, the screen width (and height) could also be detected with the <TAB><BS> technic.
No need for that.

Code: Select all

for /f "tokens=1,2 delims=: " %%A in ('mode con ^| findstr "Lines Columns"') do set %%A=%%B
Does that need to be adjusted for language?

Also, does the output of ECHO or VERIFY or KEYS change with language?


Dave Benham

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Detect echo state without temporary file

#5 Post by jeb » 25 Jan 2018 07:56

dbenham wrote:
25 Jan 2018 06:45
No need for that.

Code: Select all

for /f "tokens=1,2 delims=: " %%A in ('mode con ^| findstr "Lines Columns"') do set %%A=%%B
Okay, that's really simple :o
dbenham wrote:
25 Jan 2018 06:45
Does that need to be adjusted for language?

Also, does the output of ECHO or VERIFY or KEYS change with language?
For ECHO it's irrelevant, as it only uses the output or no output from phase 3.

But for KEYS and VERIFY it's language dependent.
German texts:
VERIFY ist ausgeschaltet (OFF).
VERIFY ist eingeschaltet (ON).
KEYS ist ausgeschaltet (OFF).
KEYS ist eingeschaltet (ON).
It would fail when the language dependent part for ON ("eingeschaltet") is one character longer than for OFF ("ausgeschaltet").
In german it fits but I suppose there exists at least one language where it fails.

dbenham
Expert
Posts: 2461
Joined: 12 Feb 2011 21:02
Location: United States (east coast)

Re: Detect echo state without temporary file

#6 Post by dbenham » 25 Jan 2018 08:58

With a bit of extra work, the VERIFY and KEYS tests can auto compensate for the language if you capture the output from a new command session:

Code: Select all

for %%V in (verifyOn verifyOff keysOn keysOff) do set "%%V="
for /f "delims=" %%A in ('"@verify on&verify&verify off&verify&keys on&keys&keys off&keys"') do (
  if not defined verifyOn (
    set "verifyOn=%%A"
  ) else if not defined verifyOff (
    set "verifyOff=%%A"
  ) else if not defined keysOn (
    set "keysOn=%%A"
  ) else set "keysOff=%%A"
)
But as has already been said, it can't work if the ON and OFF messages have the same length

I think it is trivial (but unreliable) to get the KEYS state by examining the KEYS variable. If it is defined, then you have your answer. If undefined, then it should be the default value, which I think is always OFF (unless there is a registry entry that defines the default). But there is nothing to prevent someone from spoofing the value by explicitly defining (or clearing) the KEYS variable.


Dave Benham

aGerman
Expert
Posts: 4654
Joined: 22 Jan 2010 18:01
Location: Germany

Re: Detect echo state without temporary file

#7 Post by aGerman » 25 Jan 2018 11:05

dbenham wrote:
25 Jan 2018 06:45
Does that need to be adjusted for language?
Yes this is language dependent.

Code: Select all

C:\>mode con

Status von Gerät CON:
---------------------
    Zeilen:          9001
    Spalten:         120
    Wiederholrate:   31
    Verzögerungszeit:0
    Codepage:        850


C:\>
In that and similar cases it might help to determine the line numbers of the output.

Code: Select all

for /f "tokens=1,3 delims=:" %%i in ('mode con^|findstr /n .') do if %%i==4 (set /a lines=%%j) else if %%i==5 set /a cols=%%j
Steffen

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Detect echo state without temporary file

#8 Post by penpen » 25 Jan 2018 14:36

Great work!

Just one nitpick:
Detecting the echo state only works (at least for my Win10 x64 home) only if i enabled "Use legacy console (requires relaunch)", else i get:

Code: Select all

EchoState_test1 ON
EchoState_test2 ON
penpen

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Detect echo state without temporary file

#9 Post by jeb » 17 Oct 2021 10:57

I thought again about this topic, after getting a comment from Jean-François Larvoire on Was ECHO ON or OFF when my Windows .bat was CALLed?.

I posted at stackoverflow a new technique to detect it without the need of a temporary file and without any visual effects, but the cursor moves a little bit around.
This solution is good, but still could break the screen output in some obscure situations (very small windows or screen regions are defined).

Now I developed a new way, without the need of moving the cursor at all.

Code: Select all

@echo off

REM *** Create the escape character (0x1B)
for /F "delims=#" %%a in ('prompt #$E# ^& for %%a in ^(1^) do rem') do set "ESC=%%a"

echo OFF
@call :get_echo_state
@echo state=%state%
@echo(--------------------

echo ON
@call :get_echo_state
@echo off
@echo state=%state%
@exit /b

:get_echo_state
@(
    REM *** setlocal to restore the prompt later
    setlocal
    REM *** \e[0c  places \e[?1;0c into the input buffer
    REM *** \e]    Start of an escape sequence to suppress the start LF from the prompt output
    set /p "=%ESC%[0c%ESC%]"

    REM *** Only if the prompt is displayed:
    REM *** \e[6n  places \e[<cursor-line>;<cursor-row>R into the input buffer
    REM *** \e]    Start of an escape sequence to suppress the "REM and terminating LF from the prompt output
    set "prompt=%ESC%[6n%ESC%]"

    REM *** This creates a prompt and REM output, only when "ECHO is ON"
    for %%a in (1) do REM

    REM *** After a prompt output, this cancels the \e] sequence
    REM *** The -1 in \e[-1E itself is invalid, results in no output
    set /p "=%ESC%[-1E"

    REM *** Restore the prompt
    endlocal
) <NUL > CON

@(
    set "state=on"
    REM *** Consume the first two characters from the response
    pause
    pause
    REM *** Read the next character to [char]
    REM *** If it is "?", then it's from \e[?1;0c
    for /F "tokens=1 skip=1 eol=" %%C in ('"replace /w ? . "') do @(
        if "%%C" == "?" @set "state=off"
    )
    exit /b
) < CON > NUL
The trick is to send always "ESC [ 0 c" (Query State of Device Attributes), output always "ESC [ ? 1 ; 0 c" to the input buffer
And only when "ECHO is ON" send "ESC [ 6 n" (Report Cursor Position), output "ESC [ <r> ; <c> R" to the input buffer r=row, c=column

Both places their output in front of the input buffer, the last one wins.
See also Microsoft: Console Virtual Terminal Sequences

Now it's only necessary to read the third character to detect the ECHO-state, I used here (partially) the pure batch way to read the cursor position

Aacini
Expert
Posts: 1885
Joined: 06 Dec 2011 22:15
Location: México City, México
Contact:

Re: Detect echo state without temporary file

#10 Post by Aacini » 17 Oct 2021 12:13

Many years ago, in the time of command.com, I achieved to permanently disable the ECHO status by modifing one byte in command.com file. I am pretty sure that the same mod could be done in current cmd.exe. I don't know if this could be useful, or even related to this topic, but I remember this point and I wanted to post it...

Antonio

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Detect echo state without temporary file

#11 Post by siberia-man » 21 Oct 2021 17:13

Honestly, not everything is clear for me here. The solution is extremely tricky. But one thing I found seems a bit buggy and requires to be fixed: the replace command use. It works fine until any file named as one character appears.

No one-character named file

Code: Select all

C:\Temp>dir /b ?

C:\Temp>replace /w ? . || echo BAD
Press any key to continue . . .
Create the file named as "z"

Code: Select all

C:\Temp>echo:>z

C:\Temp>replace /w ? . || echo BAD
Press any key to continue . . .

Replacing C:\Temp\z
Extended Error 32
BAD
**********

Best workaround (nothing to STDERR, intact ERRORLEVEL and the command is still short):

Code: Select all

C:\Temp>replace /w /u ? . || echo BAD
Press any key to continue . . .

It works fine when no such files:

Code: Select all

C:\Temp>del z

C:\Temp>replace /w /u ? . || echo BAD
Press any key to continue . . .

It works fine as well when no such directories:

Code: Select all

C:\Temp>mkdir z

C:\Temp>replace /w ? . || echo BAD
Press any key to continue . . .


jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Detect echo state without temporary file

#12 Post by jeb » 22 Oct 2021 07:55

I'm astonished about this bug, because I just copied that line from Dave's :getkey.

That nobody found this before, but I can reproduce it.

I played a bit and found another workaround

Code: Select all

replace /w "?<" .
Looks odd, but seems to work, I can't create a file which is fetched by the "?<" expression.

jeb
Expert
Posts: 1041
Joined: 30 Aug 2007 08:05
Location: Germany, Bochum

Re: Detect echo state without temporary file

#13 Post by jeb » 22 Oct 2021 08:00

jeb wrote:
22 Oct 2021 07:55
I'm astonished about this bug, because I just copied that line from Dave's :getkey.
I reread the code from :getkey, and I simply missed to copy the /u switch :!:

Code: Select all

replace.exe ? . /u /w
This code doesn't have any problems with one letter files

siberia-man
Posts: 208
Joined: 26 Dec 2013 09:28
Contact:

Re: Detect echo state without temporary file

#14 Post by siberia-man » 23 Oct 2021 03:28

One more stuff I think is important. What if the file or directory matching the "?" pattern exists with no access to regular user? For example, the file or directory is owned by another user. I played around with files owned by Administrator. The command replace /u /w works fine in this case as well.

penpen
Expert
Posts: 1991
Joined: 23 Jun 2013 06:15
Location: Germany

Re: Detect echo state without temporary file

#15 Post by penpen » 12 Nov 2021 08:35

jeb wrote:
22 Oct 2021 07:55
Looks odd, but seems to work, I can't create a file which is fetched by the "?<" expression.
Shouldn't it match any file with no extension,
or does "replace" ignore those undocumented wildcards?

Post Reply