简体   繁体   English

python3子进程错误从shell命令捕获output

[英]python3 subprocess error capturing output from shell commands

I've followed a number of questions on SO about capturing subprocess output. using git within subprocess seems to output to error instead of stdout or stderr我已经关注了一些关于捕获子进程 output 的问题。在子进程中使用 git 似乎 output 出错而不是 stdout 或 stderr

This is what I want to replicate from shell.这就是我要从 shell 复制的内容。

git add .
git commit -m 'commit message blah'
git push -u origin master

with tracking of output from each stage.从每个阶段跟踪 output。 In this example, I have no updates to commit.在此示例中,我没有要提交的更新。

$git add .
$ git commit -m 'wip'
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

as per code below, I am using根据下面的代码,我正在使用

output = run("git add .", check=True, capture_output=True, shell=True)

and checking output.returncode, but experiencing an CalledProcessError instead of values to output. any ideas?并检查 output.returncode,但遇到 CalledProcessError 而不是 output 的值。有什么想法吗? I can fudge functionality through error trapping, but it's far from optimal.我可以通过错误捕获来伪造功能,但这远非最佳。

I can't capture the "nothing to commit, working tree clean" output from 'git push', this seems weird.我无法从“git push”捕获“没有什么可提交的,工作树干净”output,这看起来很奇怪。 as are the values for '' stderr & stdout. '' stderr & stdout 的值也是如此。 returncode is a 0 after 'git commit'. returncode 在'git commit'之后是一个0。

nb: 'git add.'注意:'git 添加。' does not produce any output in shell.在 shell 中不产生任何 output。

environment: ubuntu app on windows 10. Python3, anaconda 64 bit.环境:windows 上的 ubuntu 应用程序 10. Python3,anaconda 64 位。

from subprocess import *
import sys
try:
  print("start git add.")
  #output = run("git add .", check=True, stdout=PIPE, stderr=PIPE, shell=True)
  output = run("git add .", check=True, capture_output=True, shell=True)
  print("git add completed.")
  print("output.stderr:", "'"+output.stderr.decode('utf-8')+"'")
  print("output.stdout:", "'"+output.stdout.decode('utf-8')+"'")
  print("output.stderr==None:", output.stderr==None)
  print("output.stdout==None:", output.stdout==None)
  print("output.returncode:", output.returncode)
  if output.returncode==0:
    output=None
    print("start git commit.")
    #output = run(f"git commit -m 'commit message three'", check=True, stdout=PIPE, stderr=PIPE, shell=True)
    output = run("git commit -m 'commit message three'", check=True, capture_output=True, shell=True)
    #exits to exception here when error occurs dueing git commit.
    print("git commit completed.")
    print("output.stderr:", output.stderr.decode('utf-8'))
    print("output.stdout:", output.stdout.decode('utf-8'))
    print("output.stderr==None:", output.stderr==None)
    print("output.stdout==None:", output.stdout==None)
    print("output.returncode:", output.returncode)
    if output.returncode==0:
      output=None
      print("start git push.")
      #output = run("git push -u origin master -f", check=True, stdout=PIPE, stderr=PIPE, shell=True)
      output = run("git push -u origin master -f", capture_output=True)
      print("git push completed.")
      print("output.stderr:", output.stderr)
      print("output.stdout:", output.stdout)
      print("output.stderr==None:", output.stderr==None)
      print("output.stdout==None:", output.stdout==None)
      print("output.returncode:", output.returncode)
      if output.returncode==0:
        print("success @ git push, output.returncode==0.")
except CalledProcessError as error:
  print("CalledProcessError error caught.")
  print('CalledProcessError:An exception occurred: {}'.format(error))
  print("CalledProcessError:sys.exc_info()[0]:", sys.exc_info()[0])
  print("CalledProcessError:output:", output)
  print("CalledProcessError:output.stderr:", output.stderr)
  print("CalledProcessError:output.stdout:", output.stdout)

output from this code block来自此代码块的 output

start git add.
git add completed.
output.stderr: ''
output.stdout: ''
output.stderr==None: False
output.stdout==None: False
output.returncode: 0
start git commit.
CalledProcessError error caught.
CalledProcessError:An exception occurred: Command 'git commit -m 'commit message three'' returned non-zero exit status 1.
CalledProcessError:sys.exc_info()[0]: <class 'subprocess.CalledProcessError'>
CalledProcessError:output: None
Traceback (most recent call last):
  File "<stdin>", line 15, in <module>
  File "/home/bmt/anaconda3/lib/python3.7/subprocess.py", line 487, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command 'git commit -m 'commit message three'' returned non-zero exit status 1.

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 41, in <module>
AttributeError: 'NoneType' object has no attribute 'stderr'

edit: my main error was using 'check=True' and 'shell=True' also changing the shell command from one string to a list of strings.编辑:我的主要错误是使用“check=True”和“shell=True”还将 shell 命令从一个字符串更改为字符串列表。

output = run("git add .", check=True, capture_output=True, shell=True)

to

output = run(["git", "add", "."], capture_output=True)

I've added some general comments about using Git from other programming languages as comments.我添加了一些关于使用其他编程语言的 Git 作为注释的一般性注释。 For your more specific case of calling git commit here, though, note that your error CalledProcessError occurs because git commit found nothing to commit.但是,对于您在此处调用git commit的更具体情况,请注意您的错误CalledProcessError发生是因为git commit没有发现要提交的内容。 When you did this from the shell:当您从 shell 执行此操作时:

$ git commit -m 'wip'
On branch master
Your branch is up to date with 'origin/master'.

nothing to commit, working tree clean

the git commit command returned a failure exit status, value 1. Your shell did not complain about this. git commit命令返回失败退出状态,值为 1。您的shell没有对此进行抱怨。

When you ran the same thing with:当你运行同样的东西时:

output = run("git commit -m 'commit message three'", check=True,
    capture_output=True, shell=True)

the git commit command again returned a failure exit status. git commit命令再次返回失败退出状态。 It was Python , not Git—specifically, the subprocess.run function —that raised a CalledProcessError , and it did so because you included check=True .引发 CalledProcessError 的是Python ,而不是 Git——特别是subprocess.run CalledProcessError ,它之所以这样做是因为你包含了check=True

This exception jumped out of the normal code flow and went into your except CalledProcessError section, where you tried to use the value found via the variable named output .此异常跳出正常代码流并进入您的except CalledProcessError部分,您在该部分尝试使用通过名为output的变量找到的值。 This variable was last bound to a value here:此变量最后绑定到此处的值:

    output=None
    print("start git commit.")

The variable still contains None at this point, which is why you saw:此时变量仍然包含None ,这就是您看到的原因:

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 41, in <module>
AttributeError: 'NoneType' object has no attribute 'stderr'

As a general rule, 1 if you want to capture everything—including the exit code and both stdout and stderr streams—with subprocess.run , don't set check=True , because that means jump out of normal processing, raising a CalledProcessError, if the exit code is anything other than zero .作为一般规则, 1如果您想使用subprocess.run捕获所有内容(包括退出代码以及 stdout 和 stderr 流),请不要设置check=True ,因为这意味着跳出正常处理,引发 CalledProcessError,如果退出代码不是零 That is, had you written:也就是说,你是否写过:

output = run("git commit -m 'commit message three'", capture_output=True,
    shell=True)

the whole thing would have behaved more in the way you expected, though you should consult the output.returncode value to see if git commit succeeded.整个事情的表现会更符合您的预期,但您应该查阅output.returncode值以查看git commit是否成功。

Note, too, that shell=True is usually a bad idea: it tends to introduce security holes.还要注意, shell=True通常不是一个好主意:它往往会引入安全漏洞。 The Python documentation calls this out. Python 文档指出了这一点。 In this case you could use:在这种情况下,您可以使用:

output = run(["git", "commit", "-m", "commit message three"], capture_output=True)

which avoids the need for shell=True .这避免了对shell=True的需要。 (The particular example you have here is safe as the entire string is a constant, but this is a bad habit to get into.) (你在这里的特定例子是安全的,因为整个字符串是一个常量,但这是一个坏习惯。)

Last, while git add and git commit can be pretty safely run in such a way as to be non-interactive—you can just inspect their return code value to determine success or failure— git push is trickier.最后,虽然git addgit commit可以以非交互的方式非常安全地运行——你可以只检查它们的返回码值来确定成功或git push更棘手。 The problem here is that git push requires that your (local) Git call up some other (remote) Git. Git will often do this using https/ssl or ssh. To do that , Git invokes other third-party programs, and those sometimes demand input passwords or passphrases.这里的问题是git push需要您的(本地)Git 调用其他一些(远程)Git。Git 通常会使用 https/ssl 或 ssh 来执行此操作。为此,Git 有时会调用第三方程序,有时会调用其他程序,Git要求输入密码或密码。

Because Git can use multiple different third-party programs for this, each of which has its own different options, there is no one right way to avoid it.因为 Git 可以为此使用多个不同的第三方程序,每个程序都有自己不同的选项,所以没有一种正确的方法可以避免它。 See Git keeps prompting me for a password and Git push requires username and password for various details.请参阅Git 不断提示我输入密码Git 推送需要用户名和密码以获取各种详细信息。


1 Since Python 3.5, however, you can use the error value raised here to access everything: 1从 Python 3.5 开始,但是,您可以使用此处引发的错误值来访问所有内容:

try:
    result = subprocess.run(cmd, check=True)
    ... use result.stdout etc ...
except subprocess.CalledProcessError as err:
    ... use err.stdout etc ...

but this will often lead to more duplicated code, so it's probably better to just leave out the check=True and inspect result.returncode .但这通常会导致更多重复代码,因此最好省略check=True并检查result.returncode

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM