实时读取 git clone 的输出

2 投票
5 回答
2752 浏览
提问于 2025-04-18 03:16

我需要读取git克隆过程中的状态(正在接收对象 XXX%),但是不知道怎么做。

我在使用subprocess.Popen,但不知道怎么抓取我需要的那一行:

proc = subprocess.Popen([GIT, "-C",IDIR+"/"+REPO,"clone",URL],shell=False,bufsize=0,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)

这是典型的输出:

Cloning into 'xxx'...
remote: Reusing existing pack: 5250, done.
remote: Counting objects: 1652, done.
remote: Compressing objects: 100% (1428/1428), done.
remote: Total 6902 (delta 389), reused 0 (delta 0)
Receiving objects: XXX% (6902/6902), 27.66 MiB | 1.51 MiB/s, done.
Resolving deltas: 100% (2010/2010), done.
Checking connectivity... done.
Checking out files: 100% (3266/3266), done.

编辑

我尝试了这个讨论串和另一个被建议为重复的讨论串中的所有建议,但都没有效果。

这个建议的结果是“正在克隆到'test'... 完成”,但没有其他输出:

popen = subprocess.Popen(["git", "clone", "https://github.com/xxx/test.git"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT,shell=False )
for line in popen.stdout:
  print "out newline:"
  print line
print "done"

这是没有包含任何状态信息的输出:

out newline:
Cloning into 'test'...

done

5 个回答

0

在这里,我想补充一下其他回答中提到的 stderr--progress,给大家提供一个简单的完整示例,这个示例会实时打印出 git clone 命令的执行过程:

import os
import re
import subprocess

def test():

    url = 'http://github.com/octocat/Hello-World/'

    output = subprocess.Popen(['git', 'clone', '--progress', url], stderr=subprocess.PIPE, stdout=subprocess.PIPE)

    fd = output.stderr.fileno()
    while True:
        lines = os.read(fd,1000).decode('utf-8')
        lines = re.split('\n|\r', lines)
        for l in lines:
            if l != '':
                print(l)
        if len(lines) == 1:
                break

    print('Press enter to continue.')
0

首先,我建议把标准输出和错误输出合在一起,这样所有信息都能集中在一个地方。也就是说,调用Popen的时候加上stderr=subprocess.STDOUT

其次,你需要在执行git的时候加上--progress这个选项,因为git-clone的手册上说:

--progress

Progress status is reported on the standard error stream by default when
it is attached to a terminal, unless -q is specified.  This flag forces
progress status even if the standard error stream is not directed to a
terminal.

而且你的git没有连接到终端。

所以代码应该像这样:

popen = subprocess.Popen(["git", "clone", "--progress", url],
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
for line in popen.stdout:
  print "out newline:"
  print line
print "done"

我建议你去看看关于“使用subprocess获取实时输出”这个问题的回答,这样可以让你的程序更好。

0

我觉得用 subprocess.communicate() 就可以解决这个问题。

我一般是这样做的:

process=subprocess.Popen(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stdoutput,stderror=process.communicate()
for line in stdoutput:
    if line.startswith('Receiving objects'):
        print line
1

我猜你是想在实时通信中显示一些进度信息,而这个过程还在进行中。

问题是,普通的标准输出流是有缓冲的,也就是说它会先把信息存起来,等到一定条件满足后再一起输出。你需要的是没有缓冲的输出流。你可以通过使用os模块来实现这一点,比如:

  fd = proc.stdout.fileno()
  while proc.returncode is None:
      l = os.read(fd, 1000)   # Read a bit of data
      print l
3

[根据评论进行了编辑]

使用 git clone 命令时加上 --progress 这个选项,可以正常显示输出。

这个方法对我有效。

import subprocess

popen = subprocess.Popen(["git", "clone", "--progress", "git@bitbucket.org:xxx/yyy.git"], stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in popen.stdout:
    print line,

print "done"

输出结果是

$ python test.py 
Cloning into 'yyy'...
remote: Counting objects: 1076, done.
remote: Compressing objects: 100% (761/761), done.
remote: Total 1076 (delta 488), reused 576 (delta 227)
Receiving objects: 100% (1076/1076), 6.24 MiB | 260.00 KiB/s, done.
Resolving deltas: 100% (488/488), done.
Checking connectivity... done.
done

[编辑]

正如 Cristian Ciupitu 指出的那样,其实你不需要使用 iter(),直接用 for line in popen.stdout: 也能正常工作(或者根据版本不同,效果可能会有所不同)。

撰写回答