174

If I had the following code:

for x in range(10):
     print(x)

I would get the output of

1
2
etc..

What I would like to do is instead of printing a newline, I want to replace the previous value and overwrite it with the new value on the same line.

ideasman42
  • 42,413
  • 44
  • 197
  • 320
ccwhite1
  • 3,625
  • 8
  • 36
  • 47
  • 1
    possible duplicate of [Python - Remove and Replace Printed items](http://stackoverflow.com/questions/5290994/python-remove-and-replace-printed-items) – Sven Marnach Mar 24 '11 at 12:54
  • 2
    As a side note, ensure you get the answer which able to flush entire line if previous line **longer** than current line. – 林果皞 Jun 18 '20 at 20:11

19 Answers19

223

Simple Version

One way is to use the carriage return ('\r') character to return to the start of the line without advancing to the next line.

Python 3

for x in range(10):
    print(x, end='\r')
print()

Python 2.7 forward compatible

from __future__ import print_function
for x in range(10):
    print(x, end='\r')
print()

Python 2.7

for x in range(10):
    print '{}\r'.format(x),
print

Python 2.0-2.6

for x in range(10):
    print '{0}\r'.format(x),
print

In the latter two (Python 2-only) cases, the comma at the end of the print statement tells it not to go to the next line. The last print statement advances to the next line so your prompt won't overwrite your final output.

Line Cleaning

If you can’t guarantee that the new line of text is not shorter than the existing line, then you just need to add a “clear to end of line” escape sequence, '\x1b[1K' ('\x1b' = ESC):

for x in range(75):
    print('*' * (75 - x), x, end='\x1b[1K\r')
print()
Guillaume Algis
  • 10,705
  • 6
  • 44
  • 72
Mike DeSimone
  • 41,631
  • 10
  • 72
  • 96
  • This works, but I don't understand a couple of the parts... '{0}' and .format – ccwhite1 Mar 24 '11 at 13:21
  • 8
    That is introduced in Python 2.6 and (I'm told) is the standard way of formatting strings in Python 3; the `%` operator is deprecated. Basically, all strings now have a [format](http://docs.python.org/library/stdtypes.html#str.format) method. In this case, the `{0}` means "the first argument to `format()`" (counting starts at 0). – Mike DeSimone Mar 24 '11 at 22:01
  • Not an option if you have to use some Windows-only software and don't have the chops to run a VM. – Mike DeSimone Jan 26 '16 at 04:34
  • From another thread, `sys.stdout.write("\033[K")` can clear the line before you start printing. Useful if you print something shorter than before. – Alecz Aug 28 '16 at 03:37
  • 3
    And if you want to be cross-terminal, `re.sub(r'\$<\d+>[/*]?', '', curses.tigetstr('el') or '')` will give you the correct erase-to-end-of-line string for whatever terminal the user has. Remember to initialize `curses` with something like `curses.setupterm(fd=sys.stdout.fileno())`, and use `sys.stdout.isatty()` to make sure your output isn't being redirected to a file. See http://code.activestate.com/recipes/475116/ for a full Python module with cursor control and color support. – Mike DeSimone Aug 31 '16 at 04:10
  • I noticed it doesn't work if you use `time.sleep()` after the `print "something\r",` statement. The caret returns to the beginning of the line, but it won't overwrite. Try the following: `print "Thing to erase\r", ; time.sleep(1) ; print "--------------\r",` and you will notice it doesn't replace with dashes. (current environment is Windows 7) – PhilMacKay Feb 08 '17 at 14:12
  • 1
    @PhilMacKay: I tried that in python on my Mac: In [3]: print "Thing to erase\r", ; time.sleep(1) ; print "--------------\r", -------------- I think your problem is Windows and its different line ends. Try this: `import curses; curses.setupterm(fd=sys.stdout.fileno()); print(hex(curses.tigetstr('cr')));` It should print the hex codes of the character sequence to go to the start of the line. – Mike DeSimone Mar 22 '17 at 13:30
  • 1
    I don't see the need for format in python 3. wouldn't `end='\r'` work just as well? – Tim Seguine Aug 28 '20 at 12:54
  • 1
    If the new line is shorter than the previous line, the tail of the previous line will remain – Elliott B Sep 03 '20 at 00:51
  • @ElliottB Fixed. – Mike DeSimone Sep 04 '20 at 01:18
  • 1
    Thanks! I didn't know about that code, I was just using a bunch of spaces before – Elliott B Sep 04 '20 at 01:19
  • @ElliottB It works on any terminal that handles VTxxx or ANSI control codes. If you get a totally dumb terminal... you have to use the spaces. – Mike DeSimone Sep 05 '20 at 03:25
  • I would like to note that if you try to print a message that fills the entire console width, the carriage return won't work. Your message has to be less than the console width (which I discovered after a couple hours of debugging). I believe this is due to the fact that the carriage return has to be on the same line as the message to clear the message out. – CrazyVideoGamer Jun 24 '23 at 01:15
112

Since I ended up here via Google but am using Python 3, here's how this would work in Python 3:

for x in range(10):
    print("Progress {:2.1%}".format(x / 10), end="\r")

Related answer here: How can I suppress the newline after a print statement?

Community
  • 1
  • 1
Pascal
  • 16,846
  • 4
  • 60
  • 69
  • 4
    I tried this and it mostly works fine. The only problem is it's not erasing prior output on that line, for instance: if your first loop print `testing` and your second loop print `test` the output after the second pass will still be `testing` - now I see that @Nagasaki45 pointed this out – Eric Uldall Jun 24 '15 at 21:35
  • 20
    in an ipython notebook, i have to do: `print("\r", message, end="")` – william_grisaitis May 19 '16 at 14:51
  • 2
    This works great, it does not erase the line but using enough whitespaces in the following print does the job (although it's not elegant). One comment I might add is that this does not work in VS Code's debug console, but it works in the terminal. – PhilMacKay Jan 09 '18 at 21:57
  • @EricUldall and @PhilMacKay ,the trick is to print some space after your message . For example: `print("Progress {:2.1%} ".format(x), end="\r")` So when the length of x change from 3 digits to 2 digits, the trailing space will erase the extra number. – Ben L Sep 21 '22 at 23:27
35

@Mike DeSimone answer will probably work most of the time. But...

for x in ['abc', 1]:
    print '{}\r'.format(x),

-> 1bc

This is because the '\r' only goes back to the beginning of the line but doesn't clear the output.

If POSIX support is enough for you, the following would clear the current line and leave the cursor at its beginning:

print '\x1b[2K\r',

It uses ANSI escape code to clear the terminal line. More info can be found in wikipedia and in this great talk.

Other approach

This other, (arguably worse) solution I have found looks like this:

last_x = ''
for x in ['abc', 1]:
    print ' ' * len(str(last_x)) + '\r',
    print '{}\r'.format(x),
    last_x = x

-> 1

One advantage is that it will work on windows too.

Neuron
  • 5,141
  • 5
  • 38
  • 59
Nagasaki45
  • 2,634
  • 1
  • 22
  • 27
  • 1
    I made a function out of the 'old answer' that works properly (even on Windows), see my answer: http://stackoverflow.com/a/43952192/965332 – erb May 13 '17 at 10:48
29

I had the same question before visiting this thread. For me the sys.stdout.write worked only if I properly flush the buffer i.e.

for x in range(10):
    sys.stdout.write('\r'+str(x))
    sys.stdout.flush()

Without flushing, the result is printed only at the end out the script

Sнаđошƒаӽ
  • 16,753
  • 12
  • 73
  • 90
user2793078
  • 419
  • 7
  • 12
  • Also good idea to fill rest of the string with spaces like `stringvar.ljust(10,' ')` in case of strings with variable length. – Annarfych Jan 31 '19 at 19:11
  • @chris, Don et al. this is Python 2, most of the other answers are Python 3. Everyone should be using Python 3 now; Python 2 is end-of-life in 2020. – smci Aug 04 '19 at 09:20
18

Suppress the newline and print \r.

print 1,
print '\r2'

or write to stdout:

sys.stdout.write('1')
sys.stdout.write('\r2')
ashastral
  • 2,818
  • 1
  • 21
  • 32
Ignacio Vazquez-Abrams
  • 776,304
  • 153
  • 1,341
  • 1,358
11
for x in range(10):
    time.sleep(0.5) # shows how its working
    print("\r {}".format(x), end="")

time.sleep(0.5) is to show how previous output is erased and new output is printed "\r" when its at the start of print message , it gonna erase previous output before new output.

Zim
  • 410
  • 4
  • 13
Rubixred
  • 149
  • 1
  • 4
10

Here's a cleaner, more "plug-and-play", version of @Nagasaki45's answer. Unlike many other answers here, it works properly with strings of different lengths. It achieves this by clearing the line with just as many spaces as the length of the last line printed print. Will also work on Windows.

def print_statusline(msg: str):
    last_msg_length = len(getattr(print_statusline, 'last_msg', ''))
    print(' ' * last_msg_length, end='\r')
    print(msg, end='\r')
    sys.stdout.flush()  # Some say they needed this, I didn't.
    setattr(print_statusline, 'last_msg', msg)

Usage

Simply use it like this:

for msg in ["Initializing...", "Initialization successful!"]:
    print_statusline(msg)
    time.sleep(1)

This small test shows that lines get cleared properly, even for different lengths:

for i in range(9, 0, -1):
    print_statusline("{}".format(i) * i)
    time.sleep(0.5)
erb
  • 14,503
  • 5
  • 30
  • 38
  • 5
    This is a great solution as it elegantly cleans the previous output to its full length without just printing an arbitrary number of white spaces (which is what I had previously resorted to!) Thanks – Toby Petty May 01 '19 at 21:19
  • I have never seen an attribute attached to a function before. nice trick. – niid Jan 30 '23 at 17:29
9

Try this:

import time
while True:
    print("Hi ", end="\r")
    time.sleep(1)
    print("Bob", end="\r")
    time.sleep(1)

It worked for me. The end="\r" part is making it overwrite the previous line.

WARNING!

If you print out hi, then print out hello using \r, you’ll get hillo because the output wrote over the previous two letters. If you print out hi with spaces (which don’t show up here), then it will output hi. To fix this, print out spaces using \r.

E.T.
  • 614
  • 6
  • 16
  • 1
    This doesn't work for me. I coped that code, but my output is `Hi BobHi BobHi BobHi BobHi Bob`. Python 3.7.1 – PaulMag Apr 16 '19 at 14:14
  • 1
    But Rubixred's answer worked. Weird. They look like the same method to me. The only difference I see is that `\r` is at the beginning instead of the end. – PaulMag Apr 16 '19 at 14:21
8

This works on Windows and python 3.6

import time
for x in range(10):
    time.sleep(0.5)
    print(str(x)+'\r',end='')
Siddhant Sadangi
  • 125
  • 4
  • 11
7

I couldn't get any of the solutions on this page to work for IPython, but a slight variation on @Mike-Desimone's solution did the job: instead of terminating the line with the carriage return, start the line with the carriage return:

for x in range(10):
    print '\r{0}'.format(x),

Additionally, this approach doesn't require the second print statement.

David Marx
  • 8,172
  • 3
  • 45
  • 66
  • Even that doesn't work in PyDev console, nor does a comma after print()'s close parenthesis. – Noumenon Jul 17 '15 at 23:16
  • didn't work for me, it just writes the final result (ex: "Download progress 100%") once the process thing is done. – Alexandre Jan 15 '16 at 01:12
  • 2
    @Alexandre It works fine in IPython or its successor Jupyter. You need to flush the buffer. http://stackoverflow.com/questions/17343688/how-to-flush-the-printed-statements-in-ipython – March Ho Mar 03 '16 at 05:01
5

The accepted answer is not perfect. The line that was printed first will stay there and if your second print does not cover the entire new line, you will end up with garbage text.

To illustrate the problem save this code as a script and run it (or just take a look):

import time

n = 100
for i in range(100):
    for j in range(100):
        print("Progress {:2.1%}".format(j / 100), end="\r")
        time.sleep(0.01)
    print("Progress {:2.1%}".format(i / 100))

The output will look something like this:

Progress 0.0%%
Progress 1.0%%
Progress 2.0%%
Progress 3.0%%

What works for me is to clear the line before leaving a permanent print. Feel free to adjust to your specific problem:

import time

ERASE_LINE = '\x1b[2K' # erase line command
n = 100
for i in range(100):
    for j in range(100):
        print("Progress {:2.1%}".format(j / 100), end="\r")
        time.sleep(0.01)
    print(ERASE_LINE + "Progress {:2.1%}".format(i / 100)) # clear the line first

And now it prints as expected:

Progress 0.0%
Progress 1.0%
Progress 2.0%
Progress 3.0%
mimoralea
  • 9,590
  • 7
  • 58
  • 59
3

I'm a bit surprised nobody is using the backspace character. Here's one that uses it.

import sys
import time

secs = 1000

while True:
    time.sleep(1)  #wait for a full second to pass before assigning a second
    secs += 1  #acknowledge a second has passed

    sys.stdout.write(str(secs))

    for i in range(len(str(secs))):
        sys.stdout.write('\b')
2

Here's my solution! Windows 10, Python 3.7.1

I'm not sure why this code works, but it completely erases the original line. I compiled it from the previous answers. The other answers would just return the line to the beginning, but if you had a shorter line afterwards, it would look messed up like hello turns into byelo.

import sys
#include ctypes if you're on Windows
import ctypes
kernel32 = ctypes.windll.kernel32
kernel32.SetConsoleMode(kernel32.GetStdHandle(-11), 7)
#end ctypes

def clearline(msg):
    CURSOR_UP_ONE = '\033[K'
    ERASE_LINE = '\x1b[2K'
    sys.stdout.write(CURSOR_UP_ONE)
    sys.stdout.write(ERASE_LINE+'\r')
    print(msg, end='\r')

#example
ig_usernames = ['beyonce','selenagomez']
for name in ig_usernames:
    clearline("SCRAPING COMPLETE: "+ name)

Output - Each line will be rewritten without any old text showing:

SCRAPING COMPLETE: selenagomez

Next line (rewritten completely on same line):

SCRAPING COMPLETE: beyonce
kpossibles
  • 21
  • 2
2

(Python3) This is what worked for me. If you just use the \010 then it will leave characters, so I tweaked it a bit to make sure it's overwriting what was there. This also allows you to have something before the first print item and only removed the length of the item.

print("Here are some strings: ", end="")
items = ["abcd", "abcdef", "defqrs", "lmnop", "xyz"]
for item in items:
    print(item, end="")
    for i in range(len(item)): # only moving back the length of the item
        print("\010 \010", end="") # the trick!
        time.sleep(0.2) # so you can see what it's doing
RenegadeJr
  • 21
  • 2
2

One more answer based on the prevous answers.

Content of pbar.py: import sys, shutil, datetime

last_line_is_progress_bar=False


def print2(print_string):
    global last_line_is_progress_bar
    if last_line_is_progress_bar:
        _delete_last_line()
        last_line_is_progress_bar=False
    print(print_string)


def _delete_last_line():
    sys.stdout.write('\b\b\r')
    sys.stdout.write(' '*shutil.get_terminal_size((80, 20)).columns)
    sys.stdout.write('\b\r')
    sys.stdout.flush()


def update_progress_bar(current, total):
    global last_line_is_progress_bar
    last_line_is_progress_bar=True

    completed_percentage = round(current / (total / 100))
    current_time=datetime.datetime.now().strftime('%m/%d/%Y-%H:%M:%S')
    overhead_length = len(current_time+str(current))+13
    console_width = shutil.get_terminal_size((80, 20)).columns - overhead_length
    completed_width = round(console_width * completed_percentage / 100)
    not_completed_width = console_width - completed_width
    sys.stdout.write('\b\b\r')

    sys.stdout.write('{}> [{}{}] {} - {}% '.format(current_time, '#'*completed_width, '-'*not_completed_width, current,
                                        completed_percentage),)
    sys.stdout.flush()

Usage of script:

import time
from pbar import update_progress_bar, print2


update_progress_bar(45,200)
time.sleep(1)

update_progress_bar(70,200)
time.sleep(1)

update_progress_bar(100,200)
time.sleep(1)


update_progress_bar(130,200)
time.sleep(1)

print2('some text that will re-place current progress bar')
time.sleep(1)

update_progress_bar(111,200)
time.sleep(1)

print('\n') # without \n next line will be attached to the end of the progress bar
print('built in print function that will push progress bar one line up')
time.sleep(1)

update_progress_bar(111,200)
time.sleep(1)
Krisz
  • 701
  • 7
  • 23
2

Better to overwrite the whole line otherwise the new line will mix with the old ones if the new line is shorter.

import time, os
for s in ['overwrite!', 'the!', 'whole!', 'line!']:
    print(s.ljust(os.get_terminal_size().columns - 1), end="\r")
    time.sleep(1)

Had to use columns - 1 on Windows.

Carl Chang
  • 119
  • 5
2

This worked for me, using Python 3.7.9 within Spyder, in Windows:

from IPython.display import clear_output
from time import sleep

def print_and_overwrite(text):
    '''Remember to add print() after the last print that you want to overwrite.'''
    clear_output(wait=True)
    print(text, end='\r')

for i in range(15):
    #I print the results backwards (from 15 to 1), to test shorter strings
    message = "Iteration %d out of 15" %(15-i)
    print_and_overwrite(message)
    sleep(0.5)

print() #This stops the overwriting
print("This will be on a new line")
Andres Silva
  • 874
  • 1
  • 7
  • 26
0

Anyway if somebody wants to overprint (clear) a many lines previously printed in stdout, than this answer should be helpful for him. (Thanks Thijmen Dam for the nice article Overwrite Previously Printed Lines)

In ANSI console you can use special sequences:

\033[1A and \033[K

First of them lift up a cursor, second - erase a line entirely.

Example of the clearing a console (Python 3):

LINE_UP = '\033[1A'
LINE_CLEAR = '\033[K'
CONSOLE_HEIGHT = 24 #lines

def clear_console():
    for a in range(CONSOLE_HEIGHT):
        print(LINE_UP, end=LINE_CLEAR, flush=True)

or eventually simply (will clear screen and move cursor to 0,0):

print('\033[2J', end='', flush=True)

If you want just positioning cursor, then use this:

print('\033[<L>;<C>f', end='', flush=True)

where <L> and <C> are Line and Column correspondingly.

Handful reference for you ANSI escape sequences

Yan Pak
  • 1,767
  • 2
  • 19
  • 15
0

Based off the responses I seen in this thread I made a nice little class that should be good across all platforms.

import sys
    
class PrintSameLine:
    """Class to correctly print on same line.
    """
    def __init__(self):
        self.last_message = ""

    def print_msg(self, msg: str):
        print(" " * len(self.last_message), end="\r", flush=True)
        print(msg, end="\r", flush=True)
        self.last_message = msg


# Note printing in reverse so you can see how it works
prnt = PrintSameLine()

for i in reverse(range(10)):
    prnt.print_msg(str(i))
Yulian Kraynyak
  • 369
  • 4
  • 11
Jessie Wilson
  • 69
  • 2
  • 7