Page 1 of 1
Detect echo state without temporary file
Posted: 24 Jan 2018 09:55
by jeb
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
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
This solution has still some draw backs.
- For the echo state detection the cursor has to been moved to the home position
- There is some output to the screen when "ECHO is ON", while testing the state
- This solution can't be used for detecting KEYS or VERIFY state, it only works for ECHO
Re: Detect echo state without temporary file
Posted: 24 Jan 2018 22:13
by dbenham
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
Re: Detect echo state without temporary file
Posted: 25 Jan 2018 02:57
by jeb
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
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
Re: Detect echo state without temporary file
Posted: 25 Jan 2018 06:45
by dbenham
jeb wrote:Your idea is also very good, how to test VERIFY and KEYS, and obviously
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
Re: Detect echo state without temporary file
Posted: 25 Jan 2018 07:56
by jeb
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
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.
Re: Detect echo state without temporary file
Posted: 25 Jan 2018 08:58
by dbenham
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
Re: Detect echo state without temporary file
Posted: 25 Jan 2018 11:05
by aGerman
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
Re: Detect echo state without temporary file
Posted: 25 Jan 2018 14:36
by penpen
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
Re: Detect echo state without temporary file
Posted: 17 Oct 2021 10:57
by jeb
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
Re: Detect echo state without temporary file
Posted: 17 Oct 2021 12:13
by Aacini
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
Re: Detect echo state without temporary file
Posted: 21 Oct 2021 17:13
by siberia-man
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 . . .
Re: Detect echo state without temporary file
Posted: 22 Oct 2021 07:55
by jeb
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
Looks odd, but seems to work, I can't create a file which is fetched by the "?<" expression.
Re: Detect echo state without temporary file
Posted: 22 Oct 2021 08:00
by jeb
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
This code doesn't have any problems with one letter files
Re: Detect echo state without temporary file
Posted: 23 Oct 2021 03:28
by siberia-man
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.
Re: Detect echo state without temporary file
Posted: 12 Nov 2021 08:35
by penpen
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?