如何执行程序或调用系统命令?

6066

如何在Python中调用外部命令,就像在shell或命令提示符中键入它一样?

66个回答

9

使用:

import subprocess

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

它提供了易于操作的良好输出:
['Filesystem      Size  Used Avail Use% Mounted on',
 '/dev/sda6        32G   21G   11G  67% /',
 'none            4.0K     0  4.0K   0% /sys/fs/cgroup',
 'udev            1.9G  4.0K  1.9G   1% /dev',
 'tmpfs           387M  1.4M  386M   1% /run',
 'none            5.0M     0  5.0M   0% /run/lock',
 'none            1.9G   58M  1.9G   3% /run/shm',
 'none            100M   32K  100M   1% /run/user',
 '/dev/sda5       340G  222G  100G  69% /home',
 '']

9

在Python中运行外部命令有很多不同的方法,它们都有各自的优点和缺点。

我和我的同事一直在编写Python系统管理工具,因此我们需要运行很多外部命令,有时您希望它们阻塞或异步运行,超时,每秒更新等。

处理返回代码和错误的方式也不同,您可能需要解析输出并提供新输入(以 expect 类型的方式)。或者您需要将 标准输入, 标准输出标准错误 重定向到不同的 tty 中运行(例如使用 GNU Screen 时)。

因此,您可能需要编写许多外部命令的包装器。因此,这是一个我们编写的Python模块,可以处理几乎任何您想要的内容,如果不行,它非常灵活,因此您可以轻松扩展:

https://github.com/hpcugent/vsc-base/blob/master/lib/vsc/utils/run.py

它不能独立工作,需要我们的其他工具,并且多年来获得了许多专业功能,因此它可能不适合作为您的即插即用替代品,但它可以为您提供有关运行命令的Python内部工作原理以及如何处理某些情况的想法。

9
使用 subprocess.call
from subprocess import call

# Using list
call(["echo", "Hello", "world"])

# Single string argument varies across platforms so better split it
call("echo Hello world".split(" "))

9
在使用Python 3.5+中的subprocess时,在Linux上我使用以下方法解决了问题:
import subprocess

# subprocess.run() returns a completed process object that can be inspected
c = subprocess.run(["ls", "-ltrh"], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
print(c.stdout.decode('utf-8'))

正如文档中提到的那样,PIPE值是字节序列,为了正确显示它们,应考虑解码。对于Python的较新版本,text=Trueencoding='utf-8'已添加到subprocess.run()的kwargs中。
上述代码的输出为:
total 113M
-rwxr-xr-x  1 farzad farzad  307 Jan 15  2018 vpnscript
-rwxrwxr-x  1 farzad farzad  204 Jan 15  2018 ex
drwxrwxr-x  4 farzad farzad 4.0K Jan 22  2018 scripts
.... # Some other lines

8
经过一些研究,我有以下代码,对我来说非常有效。它基本上实时打印标准输出和标准错误。
stdout_result = 1
stderr_result = 1


def stdout_thread(pipe):
    global stdout_result
    while True:
        out = pipe.stdout.read(1)
        stdout_result = pipe.poll()
        if out == '' and stdout_result is not None:
            break

        if out != '':
            sys.stdout.write(out)
            sys.stdout.flush()


def stderr_thread(pipe):
    global stderr_result
    while True:
        err = pipe.stderr.read(1)
        stderr_result = pipe.poll()
        if err == '' and stderr_result is not None:
            break

        if err != '':
            sys.stdout.write(err)
            sys.stdout.flush()


def exec_command(command, cwd=None):
    if cwd is not None:
        print '[' + ' '.join(command) + '] in ' + cwd
    else:
        print '[' + ' '.join(command) + ']'

    p = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=cwd
    )

    out_thread = threading.Thread(name='stdout_thread', target=stdout_thread, args=(p,))
    err_thread = threading.Thread(name='stderr_thread', target=stderr_thread, args=(p,))

    err_thread.start()
    out_thread.start()

    out_thread.join()
    err_thread.join()

    return stdout_result + stderr_result

4
当子进程退出时,如果有一些数据被缓存,你的代码可能会丢失这些数据。建议读取至EOF而不是缓存,参见teed_call() - jfs

8

这里是调用外部命令并返回或打印命令输出的方法:

Python Subprocess 的 check_output 很适合此类操作。

运行带有参数的命令,并将其输出作为字节字符串返回。

import subprocess
proc = subprocess.check_output('ipconfig /all')
print proc

参数应该被正确地分解为列表,或者您应该明确传递 shell=True。在 Python 3.x(其中 x > 3,我想)中,您可以使用 universal_newlines=True 将输出作为适当的字符串检索,并且您可能希望切换到 subprocess.run() - tripleee

8
如果您需要从Python笔记本(如Jupyter, Zeppelin, Databricks或Google Cloud Datalab)调用Shell命令,只需使用!前缀即可。
例如,
!ls -ilF

8

如果你正在编写一个Python shell脚本并且已经安装了IPython,你可以使用感叹号前缀在IPython中运行一个shell命令:

!ls
filelist = !ls

@PeterMortensen 我认为它在DOS中不起作用,但应该可以在Cygwin中工作。 - noɥʇʎԀʎzɐɹƆ

7

更新于2015年:Python 3.5添加了subprocess.run,比subprocess.Popen更易于使用。我建议使用它。

>>> subprocess.run(["ls", "-l"])  # doesn't capture output
CompletedProcess(args=['ls', '-l'], returncode=0)

>>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
  ...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1

>>> subprocess.run(["ls", "-l", "/dev/null"], capture_output=True)
CompletedProcess(args=['ls', '-l', '/dev/null'], returncode=0,
stdout=b'crw-rw-rw- 1 root root 1, 3 Jan 23 16:23 /dev/null\n', stderr=b'')

10
"Deprecated" 不仅意味着“不再开发”,还表示“不鼓励使用”。过时的功能可能随时会破坏代码、被移除或者存在风险。在重要的代码中,你绝不能使用这些功能。弃用只是比立即删除某个功能更好的方式,因为它给程序员适应和替换弃用函数的时间。 - Misch
4
为了证明我的观点:"Deprecated since version 2.6: The commands module has been removed in Python 3. Use the subprocess module instead."(自版本2.6起已弃用:在Python 3中已删除commands模块,请改用subprocess模块。) - Misch
不用担心!Python的开发人员非常小心,他们只会在主要版本之间(即2.x和3.x之间)更改功能。我自从2004年的Python 2.4以来一直在使用commands模块,而且在今天的Python 2.7中它依然能够正常工作。 - Colonel Panic
6
“危险”并不是指该模块可能随时被移除(那是另一个问题),也不是说使用这个特定的模块有危险。然而,如果发现了安全漏洞但该模块没有进一步开发或维护,它可能会变得危险。(我不想说这个模块是否容易受到安全问题的影响,只是在讨论通常情况下被弃用的内容) - Misch

6

对于 Python 3.5+,建议使用 subprocess 模块中的 run 函数。它会返回一个 CompletedProcess 对象,您可以轻松地获取输出以及返回代码。

from subprocess import PIPE, run

command = ['echo', 'hello']
result = run(command, stdout=PIPE, stderr=PIPE, universal_newlines=True)
print(result.returncode, result.stdout, result.stderr)

3
回答:run函数是在2015年添加的。你重复了它。我认为这是被投票否决的原因。 - Greg Eremeev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接