我想编写一个函数,它将执行shell命令并以字符串形式返回其输出,无论是错误还是成功消息。我只想获得与命令行相同的结果。

执行这样的事情的代码示例是什么?

例如:

def run_command(cmd):
    # ??????

print run_command('mysqladmin create test -uroot -pmysqladmin12')
# Should output something like:
# mysqladmin: CREATE DATABASE failed; error: 'Can't create database 'test'; database exists'


评论

相关:stackoverflow.com/questions/2924310/…

#1 楼

在所有正式维护的Python版本中,最简单的方法是使用subprocess.check_output函数:
>>> subprocess.check_output(['ls', '-l'])
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

check_output运行一个仅将参数作为输入的程序。1它返回与打印到stdout完全相同的结果。如果需要将输入写入stdin,请跳至runPopen部分。如果要执行复杂的shell命令,请参阅此答案末尾的shell=True上的说明。
check_output函数可在所有正式维护的Python版本中使用。但是,对于较新的版本,可以使用更灵活的方法。
现代版本的Python(3.5或更高版本):run

如果您使用的是Python 3.5+,并且不需要向后兼容性,官方文档建议对大多数任务使用新的run功能。它为subprocess模块提供了非常通用的高级API。要捕获程序的输出,请将subprocess.PIPE标志传递给stdout关键字参数。然后访问返回的stdout对象的CompletedProcess属性:
>>> import subprocess
>>> result = subprocess.run(['ls', '-l'], stdout=subprocess.PIPE)
>>> result.stdout
b'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

返回值是bytes对象,因此,如果要使用正确的字符串,则需要decode。假设被调用的进程返回一个UTF-8编码的字符串:
>>> result.stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

如果需要,可以全部压缩为单行:
>>> subprocess.run(['ls', '-l'], stdout=subprocess.PIPE).stdout.decode('utf-8')
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'

如果要传递输入可以将stdin对象传递给该进程的bytes关键字参数:
>>> cmd = ['awk', 'length(
>>> subprocess.run(['ls', '-l'], capture_output=True, text=True).stdout
'total 0\n-rw-r--r--  1 memyself  staff  0 Mar 14 11:04 files\n'
) > 5'] >>> ip = 'foo\nfoofoo\n'.encode('utf-8') >>> result = subprocess.run(cmd, stdout=subprocess.PIPE, input=ip) >>> result.stdout.decode('utf-8') 'foofoo\n'

您可以通过传递input(捕获到stderr=subprocess.PIPE)或result.stderr(捕获到stderr=subprocess.STDOUT以及常规输出)来捕获错误。如果希望result.stdout在进程返回非零退出代码时引发异常,则可以传递run。 (或者,您也可以检查上面check=Truereturncode属性。)如果不考虑安全性,则还可以按照此答案末尾的说明通过传递result来运行更复杂的Shell命令。
更高版本的Python进一步简化了上述内容。在Python 3.7+中,以上的单行代码可以这样拼写:
subprocess.check_output(*popenargs, **kwargs)  

与旧的工作方式相比,以这种方式使用shell=True只会增加一点点复杂性。但是,现在您只需使用run函数就可以完成几乎所有您需要做的事情。
旧版本的Python(3-3.4):有关run的更多信息

如果您使用的是旧版本的Python,或者需要适度的向后兼容性,可以使用上面简要描述的check_output函数。从Python 2.7开始提供。
output = subprocess.Popen(["mycmd", "myarg"], 
                          stdout=subprocess.PIPE).communicate()[0]

接受与check_output相同的参数(请参见下文),并返回包含程序输出的字符串。该答案的开头有一个更详细的用法示例。在Python 3.5+中,Popen等同于使用check_outputrun执行check=True,并仅返回stdout=PIPE属性。
您可以传递stdout来确保错误消息包含在返回的输出中。如果不关心安全性,还可以按照此答案末尾的说明通过传递stderr=subprocess.STDOUT来运行更复杂的shell命令。
如果需要从shell=True进行管道传递或将输入传递给进程,则stderr不会完成任务。在这种情况下,请参见下面的check_output示例。
复杂的应用程序和旧版Python(2.6及更低版本):Popen

如果需要深度向后兼容性,或者需要比提供Popencheck_output,您必须直接使用run对象,这些对象封装了子流程的低级API。
Popen构造函数可以接受不带参数的单个命令,也可以接受包含命令的列表作为其第一项,后跟任意数量的参数,每个参数均作为列表中的单独项。 Popen可以帮助将字符串解析为适当格式的列表。 shlex.split对象还接受用于进程IO管理和低级配置的许多不同参数。
要发送输入和捕获输出,Popen几乎总是首选方法。如下所示:
>>> import subprocess
>>> p = subprocess.Popen(['ls', '-a'], stdout=subprocess.PIPE, 
...                                    stderr=subprocess.PIPE)
>>> out, err = p.communicate()
>>> print out
.
..
foo
>>> cmd = ['awk', 'length(
>>> subprocess.check_output('cat books/* | wc', shell=True, text=True)
' 1299377 17005208 101299376\n'
) > 5'] >>> p = subprocess.Popen(cmd, stdout=subprocess.PIPE, ... stderr=subprocess.PIPE, ... stdin=subprocess.PIPE) >>> out, err = p.communicate('foo\nfoofoo\n') >>> print out foofoo

如果设置communicate,则stdin=PIPE还允许您通过communicate将数据传递给进程:
run(cmd, [stdout=etc...], input=other_output)

注意Aaron Hall的答案表明,在某些系统上,可能需要将stdinstdoutstderr全部设置为stdin(或PIPE)才能使DEVNULL完全起作用。
在极少数情况下,您可能需要复杂的,实时输出捕获。 Vartec的答案提出了一种解决方法,但如果不仔细使用communicate之外的方法,则很容易死锁。
与上述所有功能一样,当不考虑安全性时,可以通过传递communicate来运行更复杂的shell命令。
注意事项
1。运行shell命令:shell=True参数
通常,每次调用shell=Trueruncheck_output构造函数都将执行一个程序。这意味着没有花哨的bash风格的管道。如果要运行复杂的Shell命令,则可以传递Popen,这三个功能都支持。例如:
Popen(cmd, [stdout=etc...]).communicate(other_output)

但是,这样做会引起安全问题。如果您要做的不仅仅是轻脚本编写,那么最好单独调用每个进程,然后通过
q4312078qq4312078q将每个进程的输出作为输入传递给下一个进程。
直接连接管道的诱惑力很强;抵抗它。否则,您可能会遇到僵局,或者不得不做类似这样的骇人听闻的事情。

评论


使用check_output()和communication()都必须等到该过程完成为止,而使用poll()则要获得输出。确实取决于您的需求。

–vartec
2012年4月5日在9:44

不知道这是否仅适用于更高版本的Python,但是对于我来说,变量out是类型。为了获得作为字符串的输出,我必须在打印之前将其解码,如下所示:out.decode(“ utf-8”)

– PolyMesh
2013年10月31日19:42



@par当您通过shell = True时,这对您不起作用?这个对我有用。通过shell = True时不需要shlex.split。 shlex.split适用于非shell命令。我想我要删除一点,因为这使水变得浑浊。

–senderle
17-4-10在12:00



Python 3.5+允许使用关键字参数Universal_newlines = True,该参数允许您以系统的默认编码传入和传出Unicode字符串。在3.7中,它被重命名为更明智的text = True。

–tripleee
19年6月12日在16:54



对于Python 3.6+,您可以使用subprocess.run的编码参数,而不是使用result.stdout.decode('utf-8'),而可以使用subprocess.run(['ls','-l'],stdout = subprocess.PIPE,编码='utf-8')。

–皮埃尔
19年7月2日在6:50

#2 楼

这很容易,但仅适用于Unix(包括Cygwin)和Python2.7。

import commands
print commands.getstatusoutput('wc -l file')


它返回带有(return_value,output)的元组。 />
对于适用于Python2和Python3的解决方案,请改用subprocess模块:

from subprocess import Popen, PIPE
output = Popen(["date"],stdout=PIPE)
response = output.communicate()
print response


评论


现在已弃用,但对于没有subprocess.check_output的旧python版本非常有用

–static_rtti
2012年6月13日在8:20

请注意,这是特定于Unix的。例如,它将在Windows上失败。

– Zitrax
13年1月21日在9:50

+1我必须研究python 2.4的古代版本,这非常有帮助

– StephenBoesch
2014年3月14日在22:14

什么是PIPE dude来显示完整代码:subprocess.PIPE

–凯尔·布莱恩汀(Kyle Bridenstine)
18-10-30在15:50



#3 楼

这样的事情:

def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    while(True):
        # returns None while subprocess is running
        retcode = p.poll() 
        line = p.stdout.readline()
        yield line
        if retcode is not None:
            break


请注意,我要将stderr重定向到stdout,它可能不完全是您想要的,但是我也想要错误消息。 />
此函数逐行产生(通常,您必须等待子进程完成才能获得整体输出)。

对于您的情况,用法是是:

for line in runProcess('mysqladmin create test -uroot -pmysqladmin12'.split()):
    print line,


评论


确保实现某种活动循环以获取输出,以避免在等待和调用功能中潜在的死锁。

–AndréCaron
2011年1月21日15:19

@Silver Light:您的进程可能正在等待用户的输入。尝试为stdin提供PIPE值,并在Popen返回时立即关闭该文件。

–AndréCaron
2011年1月21日15:21

-1:如果retcode为0,则为无限循环。应检查retcode是否为None。您不应该产生空字符串(即使空行也至少是一个符号'\ n'):if line:yield line。最后调用p.stdout.close()。

– jfs
2011年1月24日9:37

我用ls -l / dirname尝试了代码,列出了两个文件,但目录中有更多文件后,它中断了

– Vasilis
2013年9月30日20:01在

@fuenfundachtzig:.readlines()在读取所有输出之前不会返回,因此对于不适合内存的大输出会中断。同样,为了避免子进程退出后丢失缓冲的数据,应该有一个类似retcode是否为None的模拟:p.stdout.readlines()的yield;打破

– jfs
2013年12月21日5:15



#4 楼

Vartec的答案未读完所有行,因此我制作了一个版本,该版本可以做到:

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')


用法与接受的答案相同:

command = 'mysqladmin create test -uroot -pmysqladmin12'.split()
for line in run_command(command):
    print(line)


评论


您可以使用return iter(p.stdout.readline,b'')代替while循环

– jfs
2012年11月22日15:44

那是iter的一个很酷的用法,不知道!我更新了代码。

–马克斯·埃克曼
2012年11月28日在21:53

我很确定stdout会保留所有输出,这是带有缓冲区的流对象。在Popen完成之后,我使用了一种非常类似的技术来耗尽所有剩余的输出,在我的情况下,在执行过程中还使用poll()和readline来实时捕获输出。

–马克斯·埃克曼
2012年11月28日在21:55

我删除了我的误导性评论。我可以确认,即使子进程已经退出(p.poll()不为None),p.stdout.readline()仍可能返回非空的先前缓冲输出。

– jfs
2014年9月18日,下午3:12

此代码无效。看到这里stackoverflow.com/questions/24340877/…

–thang
2015年5月3日6:00

#5 楼

这是一个棘手但超级简单的解决方案,可在许多情况下使用:

import os
os.system('sample_cmd > tmp')
print open('tmp', 'r').read()


使用命令的输出创建一个临时文件(这里是tmp),您可以从中读取所需的输出。

注释中的附加说明:
如果是一次性作业,则可以删除tmp文件。如果需要多次执行此操作,则无需删除tmp。

os.remove('tmp')


评论


哈克但超级简单+可以在任何地方工作..可以将其与mktemp结合使用,以使其在线程情况下工作

– Prakash Rajagaopal
16-10-18在1:32

也许是最快的方法,但最好添加os.remove('tmp')以使其“无文件”。

– XuMuK
17年7月3日在16:11



@XuMuK一次性工作是对的。如果是重复性工作,则可能不需要删除

– Mehdi Saman Booy
17年7月5日在15:18

不利于并发,不利于可重入功能,不利于不像启动前那样离开系统(不清理)

– 2mia
18年7月13日在12:49



@ 2mia显然这很简单,原因是有原因的!如果要将文件用作一种共享内存,用于并发读写,这不是一个好选择。但是。。。就像拥有命令输出(例如ls或find或...)一样,这是一个不错的选择。体重如果您需要一个简单问题的快速解决方案,那是我认为最好的。如果您需要管道,则子流程将为您提高效率。

– Mehdi Saman Booy
18年7月15日在6:17

#6 楼

我遇到了同样的问题,但想出了一种非常简单的方法:

import subprocess
output = subprocess.getoutput("ls -l")
print(output)


希望对您有所帮助

注意:此解决方案是特定于subprocess.getoutput()的Python3在Python2中不起作用

评论


它以字符串形式返回命令的输出,就这么简单

– azhar22k
16 Dec 4'在7:55

当然,print是关于Python 2的声明。您应该能够确定这是Python 3的答案。

–user6516765
17年1月25日在21:07

@Dev print(s)是有效的python2。subprocess.getoutput无效。

–user48956
17年4月27日在17:46

对于大多数用例,这是人们可能想要的:易于记忆,不必对结果进行解码等。谢谢。

–bwv549
19-10-3在23:39

请注意,这被明确标记为旧版函数,对异常处理的支持较差,并且没有安全保证。

–senderle
1月30日17:16

#7 楼

您可以使用以下命令来运行任何shell命令。我已在ubuntu上使用它们。

import os
os.popen('your command here').read()


注意:自python 2.6起已弃用。现在您必须使用subprocess.Popen。以下是示例

import subprocess

p = subprocess.Popen("Your command", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0]
print p.split("\n")


评论


自2.6版起不推荐使用– docs.python.org/2/library/os.html#os.popen

–Filippo Vitale
17年5月26日在13:28

@FilippoVitale谢谢。我不知道它已被弃用。

–穆罕默德·哈桑(Muhammad Hassan)
17年5月26日在14:44

根据raspberrypi.stackexchange.com/questions/71547/…的说法,os.popen()在Python 2.6中已被弃用,但在Python 3.x中却不被弃用,因为在3.x中它是使用subprocess.Popen()实现的。

– J-L
18年8月13日在19:07

#8 楼

您的里程可能会有所不同,我尝试使用@senderle在Windows 2.6.5上的Windows中使用Vartec的解决方案,但是我遇到了错误,并且没有其他解决方案起作用。我的错误是:WindowsError: [Error 6] The handle is invalid

我发现必须为每个句柄分配PIPE才能使其返回预期的输出-以下内容对我有用。

import subprocess

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    return subprocess.Popen(cmd, 
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE).communicate()


并这样调用([0]获取元组的第一个元素stdout):

run_command('tracert 11.1.0.1')[0]


了解更多之后,我相信我需要这些管道参数,因为我正在使用不同句柄的自定义系统上工作,因此我必须直接控制所有std。

要停止控制台弹出窗口(在Windows中),请执行以下操作:

def run_command(cmd):
    """given shell command, returns communication tuple of stdout and stderr"""
    # instantiate a startupinfo obj:
    startupinfo = subprocess.STARTUPINFO()
    # set the use show window flag, might make conditional on being in Windows:
    startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
    # pass as the startupinfo keyword argument:
    return subprocess.Popen(cmd,
                            stdout=subprocess.PIPE, 
                            stderr=subprocess.PIPE, 
                            stdin=subprocess.PIPE, 
                            startupinfo=startupinfo).communicate()

run_command('tracert 11.1.0.1')


评论


有趣-这一定是Windows的事情。如果有人遇到类似错误,我将添加一条注释指向此。

–senderle
2014年5月1日14:04

如果您不从管道写入/读取数据,请使用DEVNULL而不是subprocess.PIPE,否则可能会挂起子进程。

– jfs
2014年9月9日上午10:57

#9 楼

对于以下问题,我对同一问题的风格略有不同:


捕获并返回STDOUT消息(它们在STDOUT缓冲区中累积)(即实时)。


@vartec通过使用生成器和上面的'yield'键在Python上解决了这个问题


打印所有STDOUT行(即使进程在可以完全读取STDOUT缓冲区之前就退出了)
不要浪费CPU周期以高频率轮询进程
检查子进程的返回码
打印STDERR(与STDOUT分开) ),如果我们得到一个非零的错误返回码。

我结合并调整了先前的答案,得出了以下内容:

import subprocess
from time import sleep

def run_command(command):
    p = subprocess.Popen(command,
                         stdout=subprocess.PIPE,
                         stderr=subprocess.PIPE,
                         shell=True)
    # Read stdout from subprocess until the buffer is empty !
    for line in iter(p.stdout.readline, b''):
        if line: # Don't print blank lines
            yield line
    # This ensures the process has completed, AND sets the 'returncode' attr
    while p.poll() is None:                                                                                                                                        
        sleep(.1) #Don't waste CPU-cycles
    # Empty STDERR buffer
    err = p.stderr.read()
    if p.returncode != 0:
       # The run_command() function is responsible for logging STDERR 
       print("Error: " + str(err))


此代码的执行方式与以前的答案相同:

for line in run_command(cmd):
    print(line)


评论


您介意解释增加sleep(.1)不会浪费CPU周期吗?

– Moataz Elmasry
17年8月2日在9:41

如果我们继续调用p.poll()而在两次调用之间没有任何睡眠,则通过数百万次调用此函数将浪费CPU周期。取而代之的是,我们通过告诉操作系统在接下来的1/10秒内无需打扰,从而“限制”了循环,以便它可以执行其他任务。 (p.poll()也可能会休眠,从而使我们的sleep语句变得多余)。

– The Aelfinn
17年8月2日在11:04

#10 楼

拆分用于subprocess的初始命令可能会很棘手和麻烦。

使用shlex.split()可以帮助自己。

示例命令

git log -n 5 --since "5 years ago" --until "2 year ago"

代码

from subprocess import check_output
from shlex import split

res = check_output(split('git log -n 5 --since "5 years ago" --until "2 year ago"'))
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'


没有shlex.split()的代码将如下所示

 res = check_output([
    'git', 
    'log', 
    '-n', 
    '5', 
    '--since', 
    '5 years ago', 
    '--until', 
    '2 year ago'
])
print(res)
>>> b'commit 7696ab087a163e084d6870bb4e5e4d4198bdc61a\nAuthor: Artur Barseghyan...'
 


评论


shlex.split()很方便,特别是如果您不知道shell中的引用是如何工作的;但手动将此字符串转换为列表['git','log','-n','5','-since','5年前','-直到','2年前”]如果您了解报价,一点也不难。

–tripleee
19年6月18日在6:24

#11 楼



import os
import subprocess

# Define a function for running commands and capturing stdout line by line
# (Modified from Vartec's solution because it wasn't printing all lines)
def runProcess(exe):    
    p = subprocess.Popen(exe, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    return iter(p.stdout.readline, b'')

# Get all filenames in working directory
for filename in os.listdir('./'):
    # This command will be run on each file
    cmd = 'nm ' + filename

    # Run the command and capture the output line by line.
    for line in runProcess(cmd.split()):
        # Eliminate leading and trailing whitespace
        line.strip()
        # Split the output 
        output = line.split()

        # Filter the output and print relevant lines
        if len(output) > 2:
            if ((output[2] == 'set_program_name')):
                print filename
                print line


编辑:刚刚看到了J.F. Sebastian的建议的Max Persson的解决方案。继续前进,将其合并。

评论


Popen可以接受字符串,但是您需要shell = True或参数列表,在这种情况下,您应该传递['nm',filename]而不是字符串。后者是可取的,因为外壳会增加复杂性,而在此处不提供任何价值。传递不带shell = True的字符串显然可以在Windows上运行,但是在任何下一版本的Python中都可以更改。

–tripleee
19年6月18日在6:17



#12 楼

根据@senderle的说法,如果您像我一样使用python3.6:

def sh(cmd, input=""):
    rst = subprocess.run(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, input=input.encode("utf-8"))
    assert rst.returncode == 0, rst.stderr.decode("utf-8")
    return rst.stdout.decode("utf-8")


sh("ls -a")


就像您在bash中运行命令一样

评论


您正在重新设计关键字参数check = True,universal_newlines = True。换句话说,subprocess.run()已经完成了代码所要做的一切。

–tripleee
19年6月12日在17:00

#13 楼

如果使用subprocess python模块,则可以分别处理STDOUT,STDERR和命令的返回代码。您可以看到完整的命令调用者实现的示例。当然,您可以根据需要使用try..except对其进行扩展。

下面的函数返回STDOUT,STDERR和Return代码,以便您可以在其他脚本中对其进行处理。

import subprocess

def command_caller(command=None)
    sp = subprocess.Popen(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE, shell=False)
    out, err = sp.communicate()
    if sp.returncode:
        print(
            "Return code: %(ret_code)s Error message: %(err_msg)s"
            % {"ret_code": sp.returncode, "err_msg": err}
            )
    return sp.returncode, out, err


评论


subprocess.run()的另一个差的重新实现。不要重新发明轮子。

–tripleee
19年6月12日在17:02

#14 楼

这是一个解决方案,不管您是否要在进程正在运行时打印输出,都可以使用。

我还添加了当前工作目录,这对我很有用。

希望解决方案对某人有帮助:)。
import subprocess

def run_command(cmd_and_args, print_constantly=False, cwd=None):
"""Runs a system command.

:param cmd_and_args: the command to run with or without a Pipe (|).
:param print_constantly: If True then the output is logged in continuous until the command ended.
:param cwd: the current working directory (the directory from which you will like to execute the command)
:return: - a tuple containing the return code, the stdout and the stderr of the command
"""
output = []

process = subprocess.Popen(cmd_and_args, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd)

while True:
    next_line = process.stdout.readline()
    if next_line:
        output.append(str(next_line))
        if print_constantly:
            print(next_line)
    elif not process.poll():
        break

error = process.communicate()[1]

return process.returncode, '\n'.join(output), error


评论


在python 3和2.7上工作

–Joy Jedidja Ndjama
7月29日23:34

#15 楼

例如,execute('ls -ahl')
区分三种/四种可能的返回值和OS平台:


没有输出,但运行成功
输出空行,运行成功
运行失败
输出某些内容,运行成功

下面的功能

def execute(cmd, output=True, DEBUG_MODE=False):
"""Executes a bash command.
(cmd, output=True)
output: whether print shell output to screen, only affects screen display, does not affect returned values
return: ...regardless of output=True/False...
        returns shell output as a list with each elment is a line of string (whitespace stripped both sides) from output
        could be 
        [], ie, len()=0 --> no output;    
        [''] --> output empty line;     
        None --> error occured, see below

        if error ocurs, returns None (ie, is None), print out the error message to screen
"""
if not DEBUG_MODE:
    print "Command: " + cmd

    # https://stackoverflow.com/a/40139101/2292993
    def _execute_cmd(cmd):
        if os.name == 'nt' or platform.system() == 'Windows':
            # set stdin, out, err all to PIPE to get results (other than None) after run the Popen() instance
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True)
        else:
            # Use bash; the default is sh
            p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True, executable="/bin/bash")

        # the Popen() instance starts running once instantiated (??)
        # additionally, communicate(), or poll() and wait process to terminate
        # communicate() accepts optional input as stdin to the pipe (requires setting stdin=subprocess.PIPE above), return out, err as tuple
        # if communicate(), the results are buffered in memory

        # Read stdout from subprocess until the buffer is empty !
        # if error occurs, the stdout is '', which means the below loop is essentially skipped
        # A prefix of 'b' or 'B' is ignored in Python 2; 
        # it indicates that the literal should become a bytes literal in Python 3 
        # (e.g. when code is automatically converted with 2to3).
        # return iter(p.stdout.readline, b'')
        for line in iter(p.stdout.readline, b''):
            # # Windows has \r\n, Unix has \n, Old mac has \r
            # if line not in ['','\n','\r','\r\n']: # Don't print blank lines
                yield line
        while p.poll() is None:                                                                                                                                        
            sleep(.1) #Don't waste CPU-cycles
        # Empty STDERR buffer
        err = p.stderr.read()
        if p.returncode != 0:
            # responsible for logging STDERR 
            print("Error: " + str(err))
            yield None

    out = []
    for line in _execute_cmd(cmd):
        # error did not occur earlier
        if line is not None:
            # trailing comma to avoid a newline (by print itself) being printed
            if output: print line,
            out.append(line.strip())
        else:
            # error occured earlier
            out = None
    return out
else:
    print "Simulation! The command is " + cmd
    print ""


#16 楼

可以将输出重定向到文本文件,然后将其读回。

import subprocess
import os
import tempfile

def execute_to_file(command):
    """
    This function execute the command
    and pass its output to a tempfile then read it back
    It is usefull for process that deploy child process
    """
    temp_file = tempfile.NamedTemporaryFile(delete=False)
    temp_file.close()
    path = temp_file.name
    command = command + " > " + path
    proc = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
    if proc.stderr:
        # if command failed return
        os.unlink(path)
        return
    with open(path, 'r') as f:
        data = f.read()
    os.unlink(path)
    return data

if __name__ == "__main__":
    path = "Somepath"
    command = 'ecls.exe /files ' + path
    print(execute(command))


评论


当然可以,但是为什么要这么做?以及为什么要使用shell而不是传递stdout = temp_file?

–tripleee
19年6月18日在6:25

实际上,通常来说您是对的,但在我的示例中,ecls.exe似乎部署了另一个命令行工具,因此有时这种简单的方法行不通。

–马苏德·拉希米(Masoud Rahimi)
19年6月18日在6:47

#17 楼

刚刚写了一个小bash脚本来使用curl

https://gist.github.com/harish2704/bfb8abece94893c53ce344548ead8ba5

#!/usr/bin/env bash

# Usage: gdrive_dl.sh <url>

urlBase='https://drive.google.com'
fCookie=tmpcookies

curl="curl -L -b $fCookie -c $fCookie"
confirm(){
    $curl "" | grep jfk-button-action | sed -e 's/.*jfk-button-action" href="\(\S*\)".*//' -e 's/\&amp;/\&/g'
}

$curl -O -J "${urlBase}$(confirm )"


#18 楼

我想建议使用simppl作为考虑的选项。它是一个可以通过pypi获得的模块:pip install simppl并在python3上运行。
simppl允许用户运行shell命令并从屏幕上读取输出。
开发人员建议使用三种类型的用例:


最简单的用法如下所示:

    from simppl.simple_pipeline import SimplePipeline
    sp = SimplePipeline(start=0, end=100):
    sp.print_and_run('<YOUR_FIRST_OS_COMMAND>')
    sp.print_and_run('<YOUR_SECOND_OS_COMMAND>') ```





要同时运行多个命令:

    commands = ['<YOUR_FIRST_OS_COMMAND>', '<YOUR_SECOND_OS_COMMAND>']
    max_number_of_processes = 4
    sp.run_parallel(commands, max_number_of_processes) ```






最后,如果您的项目使用cli模块,则可以直接运行另一个command_line_tool作为管道的一部分。另一个工具将在同一进程中运行,但是它将在日志中作为管道中的另一个命令出现。这使得调用其他工具的工具的调试和重构更加顺畅。

    from example_module import example_tool
    sp.print_and_run_clt(example_tool.run, ['first_number', 'second_nmber'], 
                                 {'-key1': 'val1', '-key2': 'val2'},
                                 {'--flag'}) ```


请注意,通过python的logging模块打印到STDOUT / STDERR。

以下是显示simppl如何工作的完整代码:
import logging
from logging.config import dictConfig

logging_config = dict(
    version = 1,
    formatters = {
        'f': {'format':
              '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'}
        },
    handlers = {
        'h': {'class': 'logging.StreamHandler',
              'formatter': 'f',
              'level': logging.DEBUG}
        },
    root = {
        'handlers': ['h'],
        'level': logging.DEBUG,
        },
)
dictConfig(logging_config)

from simppl.simple_pipeline import SimplePipeline
sp = SimplePipeline(0, 100)
sp.print_and_run('ls')