如何在Windows命令行中覆盖/打印在当前行?

31
在Unix系统中,我可以使用\r(回车)或\b (退格)来覆盖当前行(在shell中打印已经可见的文本)。
我能否从Python脚本在Windows命令行中实现相同的效果?
我尝试过curses模块,但它在Windows上似乎不可用。

关于Python下载进度指示器的问题,或许会对你有所帮助。 - cschol
这个回答解决了你的问题吗?动态地在一行中打印 - Elikill58
11个回答

35

是的:

import sys
import time

def restart_line():
    sys.stdout.write('\r')
    sys.stdout.flush()

sys.stdout.write('some data')
sys.stdout.flush()
time.sleep(2) # wait 2 seconds...
restart_line()
sys.stdout.write('other different data')
sys.stdout.flush()

12
如果第二组数据比第一组短,那么第一组数据的字符会留在控制台上。 - Hilal

22

我知道这已经过时了,但我想讲述一下我的版本(在我的PC上通过cmd可以运行,但无法在idle中运行),用于覆盖Python 3中的一行:

>>> from time import sleep
>>> for i in range(400):
>>>     print("\r" + str(i), end="")
>>>     sleep(0.5)

编辑: 它可以在Windows和Ubuntu上运行


数Mac OS中。 - eggmatters
IDLE还没有解决方案吗? - PouJa

14

1
IPython Notebook/Jupyter正是我一直在寻找的! - dmeu

7

我刚刚遇到了这个问题。即使在 Windows 命令提示符中,你仍然可以使用 \r,但它只会将光标移动回到上一个换行符(\n)。

如果你像这样做:

cnt = 0
print str(cnt)
while True:
    cnt += 1
    print "\r" + str(cnt)

您将获得:

0
1
2
3
4
5
...

这是因为\r只会回到上一行的开头。由于您在上一个打印语句中已经写入了一个换行符,因此您的光标从新的空行的开头移动到相同新的空行的开头。
举例来说,当您打印第一个0时,您的光标将位于此处:
0
| # <-- Cursor

当你使用\r时,光标会跳到行的开头。但是你已经在行的开头了。
解决方法是避免打印\n字符,这样你的光标就在同一行上,并且\r可以正常覆盖文本。你可以使用print 'text',来实现。逗号可以防止打印换行符。
cnt = 0
print str(cnt),
while True:
    cnt += 1
    print "\r" + str(cnt),

现在它将正确地重写行。

请注意,这是Python 2.7版本,因此有print语句。


避免换行符的方法确实解决了我的问题,谢谢! 另外,在Python 3中,可以使用print(msg, end=' ')来避免换行符。 - Thomas Lang

6

简单方法:

import sys
from time import sleep
import os

#print("\033[y coordinate;[x coordinateH Hello")
os.system('cls')
sleep(0.2)
print("\033[1;1H[]")
sleep(0.2)
print("\033[1;1H  []")
sleep(0.2)
print("\033[1;1H    []")
sleep(0.2)
print("\033[1;1H      []")
sleep(0.2)
print("\033[1;1H        []")
sleep(0.2)
print("\033[1;1H      []")
sleep(0.2)
print("\033[1;1H    []")
sleep(0.2)
print("\033[1;1H  []")
sleep(0.2)
print("\033[1;1H[]")
sleep(0.2)

但它仍然会显示旧内容... - Mahrkeenerh
是的。它只是为您提供字符位置控制的能力。您必须使用更多的空格字符进行覆盖。 - Fahri Güreşçi
Python中有很好的库可以展示百分位数,也提供更好的颜色和书写支持。 - Fahri Güreşçi

3

如果您只想更新前一行,这是一个简单的方法:

import time
for i in range(20):
    print str(i) + '\r',
    time.sleep(1)

2
最简单的方法是使用两个\r——一个在开头,一个在结尾。
for i in range(10000):
    print('\r'+str(round(i*100/10000))+'%  Complete\r'),

这将很快完成。


1
第一个\r将会被处理9999次,但是无意义。如果你不知道光标在哪里,可以在循环之前调用print('\r') 一次 - Aaron Digulla

1
在Windows上(Python 3),它似乎可以工作(不直接使用stdout):
import sys

for i in reversed(range(0,20)):
  time.sleep(0.1)
  if(i == 19):
    print(str(i), end='', file=sys.stdout)
  else:
    print("\r{0:{width}".format(str(i), width = w, fill = ' ', align = 'right'), end='', file=sys.stdout)
  sys.stdout.flush()
  w = len(str(i))

每次调用打印函数时,同一行都会被更新。

这个算法可以改进,但是它被发布是为了展示你可以做什么。你可以根据自己的需求修改这个方法。


这样做是行不通的,因为你在调用 w 之后才定义它。 - Aidan H

1

在PyCharm 2020.3和Python版本3.9上进行测试,为了覆盖已编写的打印输出,我使用以下方法:

from time import sleep

for x in range(10):
    print(f'\r {x}', end='')
    sleep(0.6)

这是我在编程中主要使用的代码。使用 end='\r' 将覆盖整个文本,忽略睡眠。

在实际情况下,我会按照以下方式进行设置:

    def progress_callback(progress):
        print(f'\rDownloading File: {progress.dlable.file_name}  Progress: ' + '{0:.2f}%'.format(progress.percent), end='')
        # `return True` if the download should be canceled
        return False

    print('\nDownload complete!)

覆盖函数后的打印输出必须在新行中,否则之前的内容将被覆盖。

1

是的,这个问题是11年前提出的,但它很酷。我喜欢即兴创作。 对nosklo的答案进行补充:

import sys
import time

def restart_line():
    sys.stdout.write("\r")
    sys.stdout.flush()

string_one = "some data that is very long..."
sys.stdout.write(string_one)
sys.stdout.flush()

time.sleep(2)
restart_line()

string_two = "shorter data"
if len(string_two) < len(string_one):
    string_two = string_two+(" "*int((len(string_one)-len(string_two))))
    # This will overwrite the characters that would be left on the console

sys.stdout.write(string_two)
sys.stdout.flush()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接