[英]How to get subprocess stdout while running git command?
我有一个用 python 编写的程序并在其中使用了 git 命令。出于某种原因,我不想使用 git-python 或其他而不是子进程。 但我目前被困在获取git clone
输出上。
我试过一些代码片段。 有些命令可以很好地使用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
,但不适用于git clone
。 有没有办法解决这个问题? 提前致谢!
UPDATE1:面对面,我只想获得git clone
的完成百分比。 不需要日志或任何日志文件。
当不写入终端时, git clone
没有任何输出到 stdout 或 stderr,除非出错。
当然,当写入终端时,它有很多输出——但这些输出是不断被覆盖的进度条。 通常,您不希望那样——这将是一大堆控制字符和重复的行。
但是,如果您确实想要它,则有两种选择。
首先,您可以使用 PTY(伪 TTY)。 您可以使用os.openpty
创建一个 PTY,然后将 PTY 明确地交给子进程。 或者您可以使用os.forkpty
,它处理分叉并自动连接 PTY,因此您所要做的就是调用os.exec
函数之一。 或者您可以使用pty
模块。 (不完全清楚哪个更便携; openpty
和forkpty
声称pty
更便携,从概念上讲它是这样设计的……但它也只在 Linux 上进行了真正的测试。)
请注意, git
想要 PTY 作为它的 stderr,而不是它的 stdout。
或者,大多数git
命令都有一个--progress
标志,即使它不是终端,也会导致它们将进度写入 stderr。 至少在此处记录的版本中,这包括clone
,但当然您应该检查man
以获取本地版本。 所以,这可能就是你所需要的。 (另请参阅--verbose
标志。)
然而,这可能没有那么好。 对我来说,当我提供一个没有附加 termcaps 的 PTY 时,我得到每一行后跟一个\\r
而没有\\n
来覆盖它; 当我使用--progress
选项时, git
会检测我的脚本碰巧运行它的任何终端的 termcaps,这意味着我最终会得到 ANSI 颜色代码以及\\r
s。
当然,无论哪种方式,我都会收到数百条我不感兴趣的无用行,但我认为这就是您想要的? (或者,也许你想使用universal_newlines='\\r'
来翻译'\\r'
到'\\n'
?那是稍微欺骗,因为这是自重写Unix的终端输出,而你假装它是经典的Mac输出......但它有效。)
我有一个类似的问题,但我也想要实时进度,因为git clone
需要很长时间(比如 5 分钟),因为它是一个大型 git 存储库。 我想为用户提供实时反馈。
这是一个 Python 3.7 工作示例:
# This will print stdout/stderr as it comes in
def run_shell_command_with_realtime_output(shell_command_string, working_dir='.'):
# print the command to be executed, just for fun
print("run_shell_command >", shell_command_string)
# run it, this will NOT block
sub_process = subprocess.Popen(shell_command_string,
shell=True,
cwd=working_dir, universal_newlines=True,
stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
# print the stdout/stderr as it comes in
while True:
# The readline() will block until...
# it reads and returns a string that ends in a '\n',
# or until the process has ended which will result in '' string
output = sub_process.stdout.readline()
if output:
print(output.strip())
elif sub_process.poll() is not None:
break
# get the return code
return_code = sub_process.wait()
# Was there an error?
if return_code != 0:
print("FYI, the subprocess had an error, you might want to do something special...")
# return the sub_process, in case the caller wants to check exit/return codes
return sub_process
事实证明git clone
似乎不像我们通常习惯的那样写入 stdout/stderr 。 相反,它使用寻呼机,这就是它更新同一行的方式,就像克隆时一样,并且 % 在同一行上递增。
所以你需要这样称呼它。
# prepare to clone the git repo
git_clone_url_with_credentials = "https://<username>:<password>@bitbucket.org/myreponame.git"
git_clone_with_progress_cmd = "git --no-pager clone --progress {}".format(git_clone_url_with_credentials)
helpers_misc.run_shell_command_with_progress(git_clone_with_progress_cmd)
那应该这样做......
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.