How to overwrite the same line in command output from batch file

12

4

I want to write some status info that replaces the last line when I do something.

In following example bat file, I want the text Step 2 to overwrite Step 1 on the same line in the command output.

echo off

echo Step 1
REM Do some stuff...

echo Step 2
REM do some other stuff...

awe

Posted 2009-12-14T07:16:25.010

Reputation: 1 009

Answers

8

This script will do exactly what you asked:

@echo off

setlocal enableextensions enabledelayedexpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "ASCII_13=%%a"

set /p "=Step 1" <NUL
REM Do some stuff...

set /p "=!ASCII_13!Step 2" <NUL
REM do some other stuff...

I found this answer by looking at the code in a batch script character manipulation library written by Dave Benham called CharLib.

This library is able to create all characters in the range 1..255.

For more info see the thread entitled "Is it possible to convert a character to its ASCII value?"

Edit 2017-4-26: It appears that the code above no longer works. I'm unsure when this behaviour would have changed, but it definitely used to work. The CR character after the = now gets stripped by the set command. Ah, it appears to be a change in the way set works between XP and later versions of Windows. See the excellent answer in Overwrite line in Windows batch file? (duplicate) for more details and extra code examples.

An alternative then is to put a non-whitespace character before the !ASCII_13! and arrange for it to be erased too, perhaps either by also inserting a backspace (which doesn't get stripped) followed by a space or by appending spaces to the end of the prompt string (which aren't stripped.)

E.g. like this:

@echo off

setlocal enableextensions enabledelayedexpansion
for /f %%a in ('copy /Z "%~dpf0" nul') do set "ASCII_13=%%a"

set /p "=Step 1" <NUL
REM Do some stuff...

set /p "=x!ASCII_13!Step 2 " <NUL
REM do some other stuff...

timfoden

Posted 2009-12-14T07:16:25.010

Reputation: 154

2For me, this just prints "Step 1Step 2" instead of having just "Step 2" on the screen. – galmok – 2016-06-07T11:34:30.210

@galmok This has begun to happen to me too, but I'm sure it never used to (as I have a script that used to work perfectly put recently has changed to the behaviour you describe.) It appears that the set command now strips any CR and LF characters (as well as spaces) after the =. Try putting a non-whitespace character between the = and the !ACSII_13! – timfoden – 2017-04-26T06:28:30.437

There is a way to do all of this... I actually came to look to see if anyone else has noticed, because I Wanted to see if they had discovered how far it went. ... Can I write up an ANSWER on stack overflow? OR.. like a Question and immediately answer it? I suppose I could write it in question form about what you could do with it.. I've got to go eat something but I'll update this with a link for you. – Brent Rittenhouse – 2019-02-19T20:06:34.473

Write !ASCII_13! after Step 1 to prevent stripping, so in step 2 you would code: set /p "=Step 2 " <NUL – Zimba – 2019-12-21T14:09:13.857

6

To answer the question:

@echo off
CALL :EVALUATE "chr(8)"
SET backspace=%result%
<nul set /p =Step 1
:: DO SOME STUFF....
<nul set /p =%backspace%
<nul set /p =2
:: DO SOME OTHER STUFF....
<nul set /p =%backspace%%backspace%%backspace%%backspace%%backspace%%backspace%
<nul set /p =End   
GOTO:EOF



:EVALUATE           -- evaluate with VBS and return to result variable
::                  -- %~1: VBS string to evaluate
:: extra info: http://groups.google.com/group/alt.msdos.batch.nt/browse_thread/thread/9092aad97cd0f917

@IF [%1]==[] ECHO Input argument missing & GOTO :EOF 

@ECHO wsh.echo "result="^&eval("%~1") > %temp%\evaluate_tmp_67354.vbs 
@FOR /f "delims=" %%a IN ('cscript //nologo %temp%\evaluate_tmp_67354.vbs') do @SET "%%a" 
@DEL %temp%\evaluate_tmp_67354.vbs
::ECHO %result%
@GOTO:EOF

Output:

End

Basically, what the script does, is writes Step 1, then goes back one place, overwrites 1, and at the end goes completely back and overwrites Step 2 with End.

This going back I do with the special ASCII character 8 which is backspace. Because I don’t know how to write it in cmd, I use the :EVALUATE function which runs the VBS Chr() function, and puts the backspace-character into the variable result. If someone knows a better way, please advise.

Davor Josipovic

Posted 2009-12-14T07:16:25.010

Reputation: 472

1In PowerShell, you can also store $host.ui.RawUI.CursorPosition into a variable and restore it later to have the cursor jump back at where you were and overwrite your output. Only an option for PowerShell, but may be a bit cleaner than your VBS stuff :-) – mihi – 2012-09-25T19:08:51.870

4

@echo off
for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do set "BSPACE=%%a"
<nul set /p =Step 1
ping 127.0.0.1 >nul
<nul set /p =%BSPACE%
<nul set /p =2
ping 127.0.0.1 >nul
<nul set /p =%BSPACE%
<nul set /p =3
ping 127.0.0.1 >nul
<nul set /p =%BSPACE%%BSPACE%%BSPACE%%BSPACE%%BSPACE%%BSPACE%
<nul set /p =End.  
pause

EXPLANATION:

for /F "tokens=1 delims=# " %%a in ('"prompt #$H# & echo on & for %%b in (1) do rem"') do set "BSPACE=%%a"

this will set the backspace character into BSPACE variable. Now to see the result, type:

echo ab%BSPACE%c

output: ac

you can use this BSPACE variable more than once to delete multiple character.

Now, if you want to set a carriage return in a variable, use

for /F "usebackq" %%a in (copy /Z "%~dpf0" nul) DO (set "cr=%%a")

to see result, type: setlocal EnableDelayedExpansion & echo asfasdhlfashflkashflksa!CR!***

the output will be: ***asfasdhlfashflkashflksa

@davour's answer above is also beautiful but @timfoden's answer did not work for me.

Sourav Ghosh

Posted 2009-12-14T07:16:25.010

Reputation: 572

@timfoden's answer didn't work because !CR! should go at the end of previous step. Also, instead of all those %BSPACE%s, you could also !CR! and fill with whitespaces: set /p "=.!CR!End. " <NUL&echo: – Zimba – 2019-12-21T14:47:03.597

3

You can't. Usually you can achieve this kind of thing by including a carriage-return character (0x0D) in the file which will put the cursor back to the first column in the same line. But in this case it doesn't work; the CR is just silently eaten.

Furthermore getting the CR in there is kinda tricky and at least here involved a text editor. I'd suggest you write a little utility program that will do this for you, it actually isn't very hard. The following little C program might suffice (if you don't need Unicode):

#include <stdio.h>

int main(int argc, char* argv[]) {
  if (argc < 2) return 1;
  printf("\r%s", argv[1]);
}

It does nothing more than printing a CR character and then the text you specify as its first argument. Usage as follows:

@echo off
<nul set /P X=Step 1
pause>nul 2>nul
over.exe "Step 2"

The last line is the call to that program. The second line is the normal batch idiom for printing text without a trailing line break (which is important in this case because otherwise you couldn't overwrite the line). You could just as well use the program above as well in that place, though.

A lightly hacky way, but the only one where you can be sure where you end up, would be to use cls prior to your step output. Will flicker but that way you always write to the upper-left. And clobber everything that was visible in the console (which is why I wouldn't recommend it); most users don't like that too much.

Joey

Posted 2009-12-14T07:16:25.010

Reputation: 36 381

Of course you can; just add carriage return after step 1 ! Furthermore, here's some code to get carriage return without involving text editor: for /f %%a in ('copy /Z "%~f0" nul') do set "CR=%%a" – Zimba – 2019-12-21T14:20:33.803

OK, I had a suspicion it was not possible to do with clean command line. If I want to make a utility do do this, I might as well put the entire thing in a utility. What I really wanted was just a visible countdown that wait 5 seconds, counting down each second till finished. – awe – 2009-12-14T07:59:53.657

4awe: If you are on Vista or later, then timeout 5 will work. – Joey – 2009-12-14T08:02:38.893

Great! thank you! timeout 5 works! – awe – 2009-12-14T08:30:07.953

4I should have posted my real issue the first time... – awe – 2009-12-14T08:31:23.177

0

Get newline character, write next step, go back to start of line, write next line:

@echo off
cls
setlocal enabledelayedexpansion
echo Here's how it's done:

for /f %%a in ('copy /Z "%~f0" nul') do set "Newline=%%a"
set /p "=Step 1!Newline!" <NUL
REM Do some stuff...
ping -n 2 localhost > nul
set /p "=Step 2!Newline!" <NUL
ping -n 2 localhost > nul
set /p "=Step 3 " <NUL
REM do some other stuff...
ping -n 2 localhost > nul
echo:
echo Done.

Zimba

Posted 2009-12-14T07:16:25.010

Reputation: 107

0

Solution 1

With Notepad++ it's possible to insert the Backspace ← character (ASCII 0x08) directly, using its ASCII Codes Insertion Panel (Edit > Character Panel).

My solution is to insert the [BS] character directly and then, like other solutions posted here, use it multiple times to delete previous characters:

Code

@ECHO OFF

SET "BACKSPACE_x7=[BS][BS][BS][BS][BS][BS][BS]"

SET /P "DUMMY_VAR=Step 1" < NUL
REM Do some stuff...

SET /P "DUMMY_VAR=%BACKSPACE_x7%Step 2" < NUL
REM Do some other stuff...

GOTO :EOF

Notepad++ screenshot

Windows batch overwrite


Output

CMD output


Solution 2

Another possibility is to use cecho (echo command with colors support) to insert a Carriage Return ↵ as a unicode character (U+000D):

Code

@ECHO OFF

SET CARRIAGE_RETURN={\u000D}

cecho Step 1
REM Do some stuff...

cecho %CARRIAGE_RETURN%Step 2
REM Do some other stuff...

GOTO :EOF

NB: cecho_x64.exe runs significantly faster on my machine than cecho.exe.

andronoid

Posted 2009-12-14T07:16:25.010

Reputation: 11

0

Here's something that has been bugging me for a while is that the normal @echo to make a bell sound after the program has finished is that it put out a newline.

I got the solution from the thread so I thought I'd share:

set /p "= <BEL>"

The editor seems to not like the character but it's <0x07> in place of the <BEL>.

More info can be gotten here or by a quick google to get the character so you can paste it into the batch file.

Ste

Posted 2009-12-14T07:16:25.010

Reputation: 361

0

You'd need a screen-cursor library like curses or ncurses, but I'm not overly familiar with their use. Both libraries were developed for Unix systems, but you can find them for Windows (in the Cygwin environment, or GnuWin32).

I don't know of any easy way to access them in a DOS-style batch file. You might be better off programming in a compiled language. Take a look at the Ncurses HOWTO to get a better idea of whether it will be useful.

quack quixote

Posted 2009-12-14T07:16:25.010

Reputation: 37 382

NO need for external libraries; works fine with CMD tools. – Zimba – 2019-12-21T14:29:36.143

curses are pretty much for terminals and terminal emulators only. They don't really map to Windows's native console API. – Joey – 2009-12-14T09:17:18.743