Page 1 of 2
Output text without linefeed, even with leading space or =
Posted: 15 Jan 2013 15:35
by jeb
Hi,
normally the way of output text without a linefeed is to use the SET /P technic.
But there are some limitations for the text, as leading whitespaces are removed and leading equal signs causes a syntax error.
Discussed by Dave here
SET /P prompt mechanics - New behavior: = makes syntax errorFor simply displaying, there is a workaround to use a dummy character in front and remove it with a backspace character,
but this doesn't work for the creation of files, as the backspace will also be outputted to the file.
But there is another technic to output lines without a linefeed.
This technic uses the ability of COPY to copy only until the first SUB/EOF character.
To avoid that the SUB character itself is copied the switch /b is used for the destination file.
Code: Select all
@echo off
setlocal EnableDelayedExpansion
call :createSub
call :echoWithoutLinefeed "=hello"
call :echoWithoutLinefeed " world"
exit /b
:echoWithoutLinefeed
> txt.tmp (echo(%~1!sub!)
copy txt.tmp /a txt2.tmp /b > nul
type txt2.tmp
del txt.tmp txt2.tmp
exit /b
:createSub
copy nul sub.tmp /a > nul
for /F %%a in (sub.tmp) DO (
set "sub=%%a"
)
del sub.tmp
exit /b
hope it helps
jeb
Re: Output text without linefeed, even with trailing space o
Posted: 15 Jan 2013 16:54
by Sponge Belly
Hi Jeb and welcome back.
Brilliant workaround for the "echo without linefeed" problem.
Wish I'd thought of it!
I had thought of using copy /a, but I didn't know how to get rid of the CtrlZ at eof. Never occurred to me to combine it with the /b switch for the destination file.
One tiny suggestion: use findstr instead of type for outputting the text to make it more Unicode-friendly.
Should do the trick.
Re: Output text without linefeed, even with leading space or
Posted: 15 Jan 2013 17:16
by dbenham
Good creative solution jeb
If using TYPE, then the COPY is not needed. TYPE will stop at the Ctrl-Z.
Can you edit the subject and body of your post and substitute leading for trailing? - they are opposites, and we are talking about leading characters in this case.
Dave Benham
Re: Output text without linefeed, even with trailing space o
Posted: 15 Jan 2013 17:58
by Sponge Belly
Oops!
I checked and find can output Unicode and findstr can't.
But find is no good either because it appends CR+LF to output.
Sorry about that...
Can type work with Unicode files from a cmd /u subshell?
I seem to recall some discussion about this before, but of course I can't remember when or where.
Wiser minds will enlighten me.
Re: Output text without linefeed, even with trailing space o
Posted: 15 Jan 2013 20:35
by DigitalSnow
This will be a very useful technique. Thank you jeb. Im gonna keep this in my notes.
Re: Output text without linefeed, even with trailing space o
Posted: 15 Jan 2013 21:56
by foxidrive
In this case it works then same 'on screen' if copy has no switches.
Copy defaults to binary mode for most filetypes now, whereas it had to be specified in the MSDOS and Win9x days if you wanted a binary copy.
Re: Output text without linefeed, even with trailing space o
Posted: 15 Jan 2013 22:34
by carlos
Thanks jeb for teach this technique.
Re: Output text without linefeed, even with leading space or
Posted: 16 Jan 2013 00:41
by jeb
dbenham wrote:Can you edit the subject and body of your post and substitute leading for trailing? - they are opposites, and we are talking about leading characters in this case.
Thanks Dave, that's my non native speaker problem: I simply translated "Anfang" to trailing.
dbenham wrote:If using TYPE, then the COPY is not needed. TYPE will stop at the Ctrl-Z.
It only seems so
, but my tests show that redirecting the output of TYPE to a file,
will copy the complete context is in the resulting file. (WIN7)
jeb
Re: Output text without linefeed, even with leading space or
Posted: 16 Jan 2013 04:57
by carlos
I redirect the output of the jeb script and it is clean, the file have not the 26 ascii character. I posted a simplified version avoid a copy, but i removed it because without the copy jeb trick and only using the type command that stop when it found the SUB character the binary ouput will have the 26 ascii character.
Re: Output text without linefeed, even with leading space or
Posted: 17 Oct 2013 16:21
by dbenham
jeb's code fails if the string contains the ! character. That can be fixed easily enough.
But I found another method:
Code: Select all
@echo off
call :echoWithoutLinefeed "=hello"
call :echoWithoutLinefeed " world!"
exit /b
:echoWithoutLinefeed
setlocal disableDelayedExpansion
set "ln=%~1"
for /f %%A in ('copy /Z "%~dpf0" nul') do set EOL=%%A^
setlocal enableDelayedExpansion
<nul set /p "=x!EOL!!ln!" >ln.tmp
findstr /v $ ln.tmp
del ln.tmp
exit /b
The temp file is only needed because FINDSTR appends <CR><LF> to piped input if the last line does not end with <LF>. If FINDSTR didn't append <CR><LF>, then I would have replaced the temp file with a pipe.
UPDATEThere is one additional limit for XP: FINDSTR on XP will display most control characters and some extended ASCII characters as dots. See
What are the undocumented features and limitations of the Windows FINDSTR command? for more info.
Dave Benham
Re: Output text without linefeed, even with leading space or
Posted: 19 Oct 2013 10:37
by dbenham
I like the SET /P + FINDSTR method, but it has problems on XP in that most control characters and many extended ASCII characters display as dots on XP.
jeb's COPY method works with any set of characters (after modifying it to used delayed expansion for the string). The only character it cannot display is the SUB character (<CTRL-Z>, 0X1a, decimal 26). But that is easily solved by adding a SET /P to print the balance of the string from the first occurrence of SUB to the end.
The :writeVar routine below should be able to print absolutely any valid batch string value stored in a variable (within typical 8k line length limits of course). The :write routine is an entry point that allows printing of most string literals. The routines should work on any Windows machine from XP onward.
Code: Select all
@echo off
setlocal disableDelayedExpansion
call :writeInitialize
call :write "=hello"
call :write " world!%$write.sub%OK!"
echo(
setlocal enableDelayedExpansion
set lf=^
set "str= hello!lf!world^!!!$write.sub!hello!lf!world"
echo(
echo str=!str!
echo(
call :write "str="
call :writeVar str
echo(
exit /b
:write Str
::
:: Write the literal string Str to stdout without a terminating
:: carriage return or line feed. Enclosing quotes are stripped.
::
:: This routine works by calling :writeVar
::
setlocal disableDelayedExpansion
set "str=%~1"
call :writeVar str
exit /b
:writeVar StrVar
::
:: Writes the value of variable StrVar to stdout without a terminating
:: carriage return or line feed.
::
:: The routine relies on variables defined by :writeInitialize. If the
:: variables are not yet defined, then it calls :writeInitialize to
:: temporarily define them. Performance can be improved by explicitly
:: calling :writeInitialize once before the first call to :writeVar
::
if not defined %~1 exit /b
setlocal enableDelayedExpansion
if not defined $write.sub call :writeInitialize
>"%$write.temp%_1.txt" (echo !str!!$write.sub!)
copy "%$write.temp%_1.txt" /a "%$write.temp%_2.txt" /b >nul
type "%$write.temp%_2.txt"
del "%$write.temp%_1.txt" "%$write.temp%_2.txt"
set "str2=!str:*%$write.sub%=%$write.sub%!"
if "!str2!" neq "!str!" <nul set /p "=!str2!"
exit /b
:writeInitialize
::
:: Defines 2 variables needed by the :write and :writeVar routines
::
:: $write.temp - specifies a base path for temporary files
::
:: $write.sub - contains the SUB character, also known as <CTRL-Z> or 0x1A
::
set "$write.temp=%temp%\writeTemp%random%"
copy nul "%$write.temp%.txt" /a >nul
for /f "usebackq" %%A in ("%$write.temp%.txt") do set "$write.sub=%%A"
del "%$write.temp%.txt"
exit /b
The only problem with the above is that it wastes time mucking with temp files, even when dealing with strings that don't cause problems with SET /P. Below is an optimized variation that only uses the COPY technique if the leading character causes problems with SET /p. Note that the problem characters vary between Windows versions. I took the approach that if the leading character can cause a problem for SET /P on any Windows version, then I resort to using jeb's COPY method.
Note that the :writeInitialize routine has a string literal that contains characters that do not post well to the bulletin board. A remark explains what the proper character sequence should be.
I've tested on two different machines. This optimized version prints non-problematic strings 50 to 100% faster than the prior simpler method. The extra code causes the problem strings to be insignificantly slower than before (at worst, 10% slower). On balance, I think this is a better method.
Code: Select all
@echo off
setlocal disableDelayedExpansion
call :writeInitialize
call :write "=hello"
call :write " world!%$write.sub%OK!"
echo(
setlocal enableDelayedExpansion
set lf=^
set "str= hello!lf!world^!!!$write.sub!hello!lf!world"
echo(
echo str=!str!
echo(
call :write "str="
call :writeVar str
echo(
exit /b
:write Str
::
:: Write the literal string Str to stdout without a terminating
:: carriage return or line feed. Enclosing quotes are stripped.
::
:: This routine works by calling :writeVar
::
setlocal disableDelayedExpansion
set "str=%~1"
call :writeVar str
exit /b
:writeVar StrVar
::
:: Writes the value of variable StrVar to stdout without a terminating
:: carriage return or line feed.
::
:: The routine relies on variables defined by :writeInitialize. If the
:: variables are not yet defined, then it calls :writeInitialize to
:: temporarily define them. Performance can be improved by explicitly
:: calling :writeInitialize once before the first call to :writeVar
::
if not defined %~1 exit /b
setlocal enableDelayedExpansion
if not defined $write.sub call :writeInitialize
set $write.special=1
if "!%~1:~0,1!" equ "^!" set "$write.special="
for /f delims^=^ eol^= %%A in ("!%~1:~0,1!") do (
if "%%A" neq "=" if "!$write.problemChars:%%A=!" equ "!$write.problemChars!" set "$write.special="
)
if not defined $write.special (
<nul set /p "=!%~1!"
exit /b
)
>"%$write.temp%_1.txt" (echo !str!!$write.sub!)
copy "%$write.temp%_1.txt" /a "%$write.temp%_2.txt" /b >nul
type "%$write.temp%_2.txt"
del "%$write.temp%_1.txt" "%$write.temp%_2.txt"
set "str2=!str:*%$write.sub%=%$write.sub%!"
if "!str2!" neq "!str!" <nul set /p "=!str2!"
exit /b
:writeInitialize
::
:: Defines 3 variables needed by the :write and :writeVar routines
::
:: $write.temp - specifies a base path for temporary files
::
:: $write.sub - contains the SUB character, also known as <CTRL-Z> or 0x1A
::
:: $write.problemChars - list of characters that cause problems for SET /P
:: <carriageReturn> <formFeed> <space> <tab> <0xFF> <equal> <quote>
:: Note that <lineFeed> and <equal> also causes problems, but are handled elsewhere
::
set "$write.temp=%temp%\writeTemp%random%"
copy nul "%$write.temp%.txt" /a >nul
for /f "usebackq" %%A in ("%$write.temp%.txt") do set "$write.sub=%%A"
del "%$write.temp%.txt"
for /f %%A in ('copy /z "%~f0" nul') do for /f %%B in ('cls') do (
set "$write.problemChars=%%A%%B ""
REM the characters after %%B above should be <space> <tab> <0xFF>
)
exit /b
Dave Benham
Re: Output text without linefeed, even with leading space or
Posted: 20 Oct 2013 20:02
by zoeyku
Brilliant workaround for the "echo without linefeed" problem.
Re: Output text without linefeed, even with leading space or
Posted: 30 Jan 2014 09:33
by carlos
I found a Little problem with the jeb code with string beginning with character ! & >
This is my fix for it:
Code: Select all
@echo off
setlocal DisableDelayedExpansion
call :echoWithoutLinefeed "=hello"
call :echoWithoutLinefeed " world"
call :echoWithoutLinefeed "!>"
pause
exit /b
:echoWithoutLinefeed
setlocal DisableDelayedExpansion
> txt.tmp cmd /a /v:on /c set /p "=_%~1" <nul
copy /y txt.tmp /b + nul /a txt.tmp >nul
type txt.tmp | (pause>nul&findstr "^">txt2.tmp)
copy /y txt2.tmp /a txt2.tmp /b >nul
type txt2.tmp
del txt.tmp txt2.tmp
endlocal
exit /b
Re: Output text without linefeed, even with leading space or
Posted: 30 Jan 2014 09:54
by dbenham
@Carlos - yes, that is true.
The code from two posts before yours also addresses the issue, and is optimized to print more quickly if there are no problem characters.
Dave Benham
Re: Output text without linefeed, even with leading space or =
Posted: 11 Jul 2021 03:26
by jeb
I started this thread, now I will finish it with a really simple method, shown by sst at
How do I add a space on this line?
Code: Select all
@echo off
setlocal
set "prompt= Hello"
cmd /d /k < nul
echo World
endlocal
It's so horrifying simple, I can't understand why nobody found that before.
jeb