2014-09-23 137 views
0

我有一个用Python编写的程序,并在其中使用git命令.. 由于某些原因,我不想使用git-python或其他子进程。 但我目前陷入git clone输出。如何在运行git命令时获得子进程标准输出?

我试过一些代码片段。一些工作正常,如ping 8.8.8.8,但不是git clone

例如

使用线程

def log_worker(stdout): 
    while True: 
     last = non_block_read(stdout).strip() 
     if last != "": 
      print(last) 


def non_block_read(output): 
    fd = output.fileno() 
    fl = fcntl.fcntl(fd, fcntl.F_GETFL) 
    fcntl.fcntl(fd, fcntl.F_SETFL, fl | os.O_NONBLOCK) 
    try: 
     return output.read() 
    except: 
     return '' 

def test(): 
    mysql_process = subprocess.Popen(
     "ping google.com", 
     shell=True, 
     stdout=subprocess.PIPE, 
     stderr=subprocess.PIPE) 

    thread = Thread(target=log_worker, args=[mysql_process.stdout]) 
    thread.daemon = True 
    thread.start() 

    mysql_process.wait() 
    thread.join(timeout=1) 

test() 

newlines = ['\n', '\r\n', '\r'] 
def unbuffered(proc, stream='stdout'): 
    stream = getattr(proc, stream) 
    with contextlib.closing(stream): 
     while True: 
      print('tt') 
      out = [] 
      last = stream.read(1) 
      # Don't loop forever 
      if last == '' and proc.poll() is not None: 
       break 
      print('last', last) 
      while last not in newlines: 
       print("loop") 
       # Don't loop forever 
       if last == '' and proc.poll() is not None: 
        break 
       out.append(last) 
       last = stream.read(1) 
      out = ''.join(out) 
      yield out 

def example(): 
    cmd = ['ls', '-l', '/'] 
    proc = subprocess.Popen(
     cmd, 
     stdout=subprocess.PIPE, 
     stderr=subprocess.STDOUT, 
     # Make all end-of-lines '\n' 
     universal_newlines=True, 
     shell = True 
    ) 
    for line in unbuffered(proc): 
     print('new line') 
     print line 

example() 

和最常见的一种

for line in iter(proc.stdout.readline, ''): 
    sys.stdout.write('{:.2f} {}\n'.format(
     time.time() - start, 
     line.rstrip() 
    )) 
    sys.stdout.flush() 

他们都工作正常ping google.com名称,但n ot git clone。 有什么办法可以解决这个问题吗? 在此先感谢!

UPDATE1: 面对面,我只想得到git clone的完成百分比。不需要日志或任何日志文件。

+0

为什么是你使用'shell = True'(特别是因为你使用的是参数列表而不是shell命令行)? – abarnert 2014-09-23 03:50:47

+1

不会subprocess.check_output是一件容易的事情吗? – 2014-09-23 03:52:31

+0

另外,哪个单词是最常见的“最常见”?你什么时候需要日志式时间戳,但不想使用'logging'模块? – abarnert 2014-09-23 04:07:15

回答

3

当不写入终端时,git clone没有任何输出到stdout或stderr,除了错误。

当写入终端时,当然,它有很多输出 - 但输出是不断被覆盖的进度条。通常情况下,你不需要这样 - 这将是一大堆控制字符和重复行。

但是,如果你想要它,有两种选择。


首先,您可以使用PTY(伪TTY)。您可以使用os.openpty创建PTY,然后将PTY显式地移交给子进程。或者您可以使用os.forkpty,它可以处理分叉和自动挂接PTY,因此您只需调用os.exec函数之一即可。或者您可以使用pty模块。 (这并不完全清楚这是更便携; openptyforkpty声称pty更便于携带,并在概念上它是这样设计的...但它也可以在Linux只有真正的考验。)

注意git希望PTY作为其标准错误,而不是它的stdout。


另外,最git命令具有--progress标志,使他们写进度,标准错误,即使它不是一个终端。至少从版本here开始,这包括clone,但是当然您应该检查man是否为您的本地版本。所以,可能是是你所需要的。 (另请参阅--verbose标志。)

但是,这可能不尽如人意。对于我来说,当我提供一个没有附加termcaps的PTY时,我得到每一行后面跟着一个\r而没有\n来覆盖它;当我使用--progress选项时,git会检测到我的脚本正在运行它的任何终端的termcaps,这意味着我最终将获得ANSI颜色代码以及\r s。


当然,无论哪种方式,我得到数百个无用的线,我有没有兴趣,但我以为这是你想要的吗? (或者你想用universal_newlines='\r'来翻译'\r''\n'?这有点作弊,因为这是自我覆盖Unix终端输出,你假装它是经典的Mac输出......但它的工作原理。)

+0

谢谢你的建议。我认为PTY解决方案在跨平台上会很痛苦吗?我的程序必须在Windows 7和MacOS上运行。 – 2014-09-23 04:24:11

+0

@RobinZhang:在Windows上无法使用PTY。 (Windows确实有类似的概念,但是Python并没有试图将这两个概念封装在一个API中。)所以我认为'--progress'是唯一的选择。但首先:这个问题是否真的发生在Windows上?许多命令行应用程序都不会在Windows上执行'isatty'检查,这意味着您可以在Unix上使用PTY并且在Windows上不做任何特殊的处理... – abarnert 2014-09-23 04:44:33

+0

对于'git clone --progress - repo' – jfs 2014-09-23 05:17:52

相关问题