我有一个用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
的完成百分比。不需要日志或任何日志文件。
答案 0 :(得分:4)
当没有写入终端时,git clone
没有任何输出到stdout或stderr,除非出错。
当写入终端时,当然它有很多输出 - 但输出是不断覆盖的进度条。通常情况下,你不会想要它 - 它会成为一大堆控制角色和重复的线条。
但如果你做想要它,有两种选择。
首先,您可以使用PTY(Pseudo-TTY)。您可以使用os.openpty
创建一个PTY,然后将PTY显式关闭到子进程。或者你可以使用os.forkpty
来处理分叉并自动连接PTY所以你所要做的就是调用os.exec
函数之一。或者您可以使用pty
模块。 (并不完全清楚哪个更便携; openpty
和forkpty
声称pty
更便于携带,而且从概念上讲它是这样设计的......但它&# 39; s也只在Linux上测试过。)
请注意git
想要将PTY作为其stderr,而不是它的标准输出。
或者,大多数git
命令都有一个--progress
标志,即使它不是终端,也会导致它们向stderr写入进度。至少在记录here的版本中,这包括clone
,但您当然应该检查本地版本的man
。所以,可能就是你所需要的。 (另请参阅--verbose
标志。)
但是,这可能不太好。对我来说,当我提供一个没有附加termcaps的PTY时,我会得到每一行,然后是\r
而不是\n
来覆盖它;当我使用--progress
选项时,git
检测到我的脚本碰巧运行它的任何终端的termcaps,这意味着我最终得到ANSI颜色代码以及\r
s。 / p>
当然,无论哪种方式,我都会收到数百条无用的线路,但我认为这是你想要的? (或者您可能希望使用universal_newlines='\r'
将'\r'
翻译为'\n'
?这有点作弊,因为这是自我覆盖的Unix终端输出,您可以使用重新假装它的经典Mac输出......但它确实有效。)
答案 1 :(得分:0)
我遇到了类似的问题,但我也想要实时进度,因为 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)
这应该可以...