jeb-the-batcher
Goto Top

Erzeugung von ESC,CR,LF und DEL mit reinem Batch

Hallo an alle Batch Interessierten.

Zu erst, wozu das Ganze?

Weil ich Spaß daran habe mit Batch Sachen hinzubekommen die in jeder Programmiersprache vollkommen simpel sind,
und ich viel Zeit gebraucht habe all diese Sachen zu entwicklen (OK, der LF-Trick ist nicht von mir).

Wozu braucht man denn [DEL] oder ein [LF] Zeichen in einem Batchfile?

Um z.B. mit einem einzelnen echo über mehrere Zeilen eine Ausgabe machen zu können,
oder mit [DEL] bereits geschriebene Zeichen wieder zu löschen, z.B. für eine prozentuale Fortschrittsanzeige.

Jetzt erst mal der Code
@echo off
setlocal EnableDelayedExpansion 
call :CreateLF
call :CreateDEL_ESC
call :CreateCR

echo Test1: The LineFeed!lf!This is in a new line
echo(
<nul set /p ".=Test2: The DEL removes a char, press a key #"  
pause > nul
echo %DEL%X - changing of # with X
echo(
echo Test3: Print the ESC %ESC%
echo(
<nul set /p ".=Test4: ##########The CR can change a line, press a key"  
pause > nul
echo !CR! It works --- 
goto :eof

::: LF should  be used only with DelayedExpansion
:CreateLF
set LF=^


rem ** The two empty lines are neccessary, spaces are not allowed
set ^"NLF=^^^%lf%%lf%^%lf%%lf%^"  
goto :eof

::: DEL and ESC can be used  with and without DelayedExpansion
:CreateDEL_ESC
setlocal
for /F "tokens=1,2 delims=#" %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (  
  ENDLOCAL
  set "DEL=%%a"  
  set "ESC=%%b"  
  goto :EOF
)
goto :eof

::: CR should  be used only with DelayedExpansion
:CreateCR
setlocal EnableDelayedExpansion EnableExtensions
set "X=."  
for /L %%c in (1,1,13) DO set X=!X:~0,4094!!X:~0,4094!

echo !X!  > %temp%\cr.tmp
echo\>> %temp%\cr.tmp
for /f "tokens=2 usebackq" %%a in ("%temp%\cr.tmp") do (  
   endlocal
   set cr=%%a
   rem set x=
   goto :eof
)
goto :eof

Jetzt zu dem eigentlichem Teil, wie funktioniert das alles.

Das einfachste ist hierbei das LineFeed (LF).
Es wird durch eine multiline Caret ^ erzeugt mit zwei Leerzeilen.
Warum zwei? Weil das ^ eigentlich ein Escapezeichen für das folgende Zeichen ist als z.B. ^& am Zeilenende
wird aber das erste Zeilenende ignoriert und die nächste Zeile eingelesen, wenn da auch nur ein Zeilenende
gefunden wird, wird dieses "Escaped" damit ist kein Zeilenende mehr da und es wird eine weitere Zeile eingelesen,
die sollte dann leer sein, sonst wird der Inhalt an das <LF> drangehängt.
Jetzt kann natürlich der Einwand kommen, dass am Zeilenende nicht nur ein Linefeed steht sondern in der Windowswelt
eine Zeile mit <CR><LF> endet.
Das an dieser Stelle, dass <LF> genommen wird, statt des <CR> liegt daran, dass <CR> generell in dieser Phase nicht mehr existieren,
die wurden vom Parser entfernt.

Das <CR> wird daher über einen ganz anderen Weg erzeugt.
Es wird ein String erzeugt, der genau an der maximalen Länge für Strings liegt.
Dadurch kann bei dem echo !X! Befehl statt des normalen <CR><LF> Zeilenendes nur noch das <CR> ausgegeben werden.
Um an genau dieses dann zu gelangen wird noch ein weiteres <CR><LF> ausgegeben.
Die temporäre Datei enthält somit "....(ca 8000 Punkte)....<CR><CR><LF>"
Dann wird die Datei mit einem FOR /F eingelesen, dabei landet das einzelne <CR> im zweiten Token.
Da in der %%var Phase die <CR> nicht mehr entfernt werden, kann das <CR> erfolgreich einer Variablen zugewiesen werden.
Bei der Ausgabe klappt dann nur !CR!, weil in der anderen Variante %CR% das <CR> sofort wieder vom Parser entfernt wird.

Um das ESC und DEL zu erzeugen wird ein vollkommen anderer Weg verwendet.
Dazu wird der prompt Befehl genutzt, dieser erlaubt den Standard-Prompt mit $H$E umzustellen auf <DEL> und <ESC>.
Damit man allerdings dieses nicht nur auf der Kommandozeile sieht, sondern auch diesen in einem Batch auswerten kann,
wird echo on verwendet um auch innerhalb einer Batchausführung den Prompt zu sehen.
Damit man dann auch noch die Ausgabe abfangen kann wird der Teil innerhalb einer For-Loop ausgeführt.

Der einzige Teil der mir bei dem Ganzen noch nicht so recht gefällt, ist die Methode um <CR> zu erzeugen, da es praktisch auf
einen Bufferüberlauf angewiesen ist. Es ist also anzunehmen, dass dieses Verhalten vielleicht irgendwann mal entfernt wird,
oder es fehlschlägt, wenn z.B. die maximale Zeilenlänge von Microsoft geändert wird.

Hoffe es war nicht zu langweilig und es finden sich sinnvolle Anwendungen

jeb-the-batcher

Content-Key: 156449

Url: https://administrator.de/contentid/156449

Printed on: April 25, 2024 at 08:04 o'clock

Member: jeb-the-batcher
jeb-the-batcher Jan 06, 2011 at 15:13:55 (UTC)
Goto Top
Ich habe eine mir neue Methode von pieh-ejdsch angeschaut, wie er das CR erzeugt. Batch Prozent der Formatierung auslesen

Daraus habe ich jetzt eine neue elegante Variante gebastelt, die keinen Bufferoverlflow mehr braucht und auch ohne temporäre Datei auskommt.
Und das ganze ist auch noch schön kurz.

::: CR should  be used only with DelayedExpansion
:CreateCR
for /F "usebackq" %%a in (`copy /Z "%~dpf0" nul`) DO (  
   set "cr=%%a"  
)
goto :eof

Und wie funktioniert's?
Der copy Befehl mit /Z erzeugt eine Ausgabe mit Fortschrittsanzeige 0% kopiert ... 100% kopiert.
Dabei wird der Fortschritt immer in eine Zeile geschrieben indem ein <CR> ausgegeben wird.
Und genau das hole ich mir, und damit ich bei der ganzen Aktion keine Datei real kopiere gebe ich als Quelle die eigene Batch-Datei an
und als Ziel NUL.

jeb - und wieder einen Schritt weiter
Member: rubberman
rubberman Jan 16, 2011 at 00:06:53 (UTC)
Goto Top
Hallo jeb.

Na das gefällt mir doch um Längen besser face-wink
Und dass das CR gleich das erste Token ist, ist ein Umstand der das ganze noch vereinfacht.

Wieder mal ist mir eine Nebensache nicht ganz klar (schließlich will man ja dazulernen).
Warum du usebackq nutzt, statt den 8.3-Namen der Batchdatei zu verwenden, leuchtet wegen möglicher Sonderzeichen ein. Warum allerdings "%~dpf0" statt "%~f0" ist mir suspekt. Unter welchen Umständen könnte letzteres fehlschlagen?

Grüße
rubberman
Member: jeb-the-batcher
jeb-the-batcher Jan 16, 2011 at 00:14:29 (UTC)
Goto Top
Hallo rubberman,

ja da hat mal einer aufgepasst face-smile aber schön, dass doch jemand die Anleitungen liest.

Es ist natürlich vollkommen sinnlos %~dpf0 zu verwenden, statt direkt %~f0.
Aber dadurch kann man auch gut sehen, dass die Reihenfolge total egal ist, die einzelnen Pfadteile scheinen nur durch Flags aktiviert zu werden.
Denn %~pddpxffx0 ergibt auch nichts anderes als %~f0.

Grüße
jeb
Member: rubberman
rubberman Jan 16, 2011 at 00:36:50 (UTC)
Goto Top
Hallo jeb.

Zitat von @jeb-the-batcher:
... aber schön, dass doch jemand die Anleitungen liest.
Oooch, knapp 1000 Hits in einem Monat ist doch mehr als nur "jemand".

Zitat von @jeb-the-batcher:
Es ist natürlich vollkommen sinnlos %~dpf0 zu verwenden, statt direkt %~f0.
Und ich dachte schon mir wäre da was entgangen.

Schönen Abend noch
rubberman

<EDIT>
Das usebackq leuchtet doch nicht ein, schließlich verarbeitest du das Output eines Befehls und nicht die Datei selbst.
for /f %%a in ('copy /z "%~f0" nul') do set "cr=%%a"  
... sollte also ebenso funktionieren.
</EDIT>
Member: jeb-the-batcher
jeb-the-batcher May 27, 2011 at 18:51:00 (UTC)
Goto Top
Da ist mir nach einem halben Jahr tatsächlich noch ein (schwerer) Fehler aufgefallen. face-sad

Die Erzeugung von <DEL> (oder auch <Backspace>,ASCII 0x08) ist nicht korrekt, denn meine Erklärung

Zitat von @jeb-the-batcher:
Um das ESC und DEL zu erzeugen wird ein vollkommen anderer Weg verwendet.
Dazu wird der prompt Befehl genutzt, dieser erlaubt den Standard-Prompt mit $H$E umzustellen auf <DEL> und <ESC>.
Damit man allerdings dieses nicht nur auf der Kommandozeile sieht, sondern auch diesen in einem Batch auswerten kann,
wird echo on verwendet um auch innerhalb einer Batchausführung den Prompt zu sehen.
Damit man dann auch noch die Ausgabe abfangen kann wird der Teil innerhalb einer For-Loop ausgeführt.

Ist an der Stelle von $H nicht richtig, denn $H ist kein einzelnes <Backspace> sondern in Wirklichkeit eine Kombination aus
<Backspace><Space><Backspace>, sprich in der DEL-Variabeln sind auch immer drei Zeichen gelandet.

Aber wenigstens ist die Korrektur recht einfach, ein zusätzliches Space bei delims reicht, somit ergibt sich
:CreateDEL_ESC
:: Creates two variables with one character DEL=Ascii-08 and ESC=Ascii-27
:: DEL and ESC can be used  with and without DelayedExpansion
:: @attention $H produce a <BS><space><BS>, so we need # and <space> as delims
setlocal
for /F "tokens=1,2 delims=# " %%a in ('"prompt #$H#$E# & echo on & for %%b in (1) do rem"') do (  
  ENDLOCAL
  set "DEL=%%a"  
  set "ESC=%%b"  
  goto :EOF
)
goto :eof

jeb
Member: rubberman
rubberman May 29, 2011 at 23:14:58 (UTC)
Goto Top
Hallo jeb,

das funktioniert bei mir so nicht. Ist auch unlogisch, denn durch Space als zusätzlichen Delimiter hast du nun im 1. und im 2. Token ein Backspace.
What about:
for /F "tokens=2,3" %%a in ('"prompt $H$S$E$S & echo on & for %%b in (1) do rem"') do (

Grüße
rubberman
Member: pieh-ejdsch
pieh-ejdsch Aug 26, 2012 at 09:21:45 (UTC)
Goto Top
moin,

Das Del aus <del><space><del> besteht ist vllt nur deswegen, weil ein Einzelnes Del im Anschluss Keinen Rückschritt macht. Wenn also hinter einem Einzelnen Del kein Zeichen ist erfolgt auch kein Rückschritt.

Das SpaceZeichen ($S) hinter dem Prompt Befehl in der For-Anweisung leuchtet ja noch ein.
Aber warum wird dort ein echo on geschalten? In der Anweisung einer Forschleife ist immer ein Echo on geschalten.
wie cmd /c im Gegensatz zu cmd /q /c.

Ich hab mir jetzt die Zähne verbissen, weil ich ein = in ein <nul set /p "==" wollte. Mal sehen das löse ich noch anders.

Gruß Phil
Member: rubberman
rubberman Aug 26, 2012 at 11:57:39 (UTC)
Goto Top
Hallo PH,

du hast recht, das ECHO ON ist unnötig.

Zu deinem Problem - teste mal:
for /f %%a in ('"prompt $H &for %%b in (1) do rem"') do set "BS=%%a"  
set /p "=.%BS%="  
Der Nachteil ist selbstverständlich, dass Punkt und Backspace bei Umleitung in eine Datei mit umgeleitet würden ...

Grüße
rubberman