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

6066

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

66个回答

18

一种简单的方法是使用os模块

import os
os.system('ls')

或者,您还可以使用subprocess模块:

import subprocess
subprocess.check_call('ls')

如果您想将结果存储在变量中,请尝试:

import subprocess
r = subprocess.check_output('ls')

17
从OpenStack Neutron中获取网络ID:
```

要从OpenStack Neutron获取网络ID:

```
#!/usr/bin/python
import os
netid = "nova net-list | awk '/ External / { print $2 }'"
temp = os.popen(netid).read()  /* Here temp also contains new line (\n) */
networkId = temp.rstrip()
print(networkId)

nova net-list的输出结果

+--------------------------------------+------------+------+
| ID                                   | Label      | CIDR |
+--------------------------------------+------------+------+
| 431c9014-5b5d-4b51-a357-66020ffbb123 | test1      | None |
| 27a74fcd-37c0-4789-9414-9531b7e3f126 | External   | None |
| 5a2712e9-70dc-4b0e-9281-17e02f4684c9 | management | None |
| 7aa697f5-0e60-4c15-b4cc-9cb659698512 | Internal   | None |
+--------------------------------------+------------+------+

print(networkId)的输出结果。

27a74fcd-37c0-4789-9414-9531b7e3f126

在2016年,你不应该推荐使用os.popen()。Awk脚本可以很容易地被本地的Python代码所替代。 - tripleee

15

运行任何命令并获取结果的最简单方法:

from commands import getstatusoutput

try:
    return getstatusoutput("ls -ltr")
except Exception, e:
    return None

4
Python 2.7 的“commands”文档确实表明它在2.6版本中被弃用,并将在3.0版本中移除。"commands"是Python中的一个模块。 - tripleee

14

大多数情况:

对于大多数情况,您只需要像这样的一小段代码:

import subprocess
import shlex

source = "test.txt"
destination = "test_copy.txt"

base = "cp {source} {destination}'"
cmd = base.format(source=source, destination=destination)
subprocess.check_call(shlex.split(cmd))

它干净简洁

subprocess.check_call使用参数运行命令并等待命令完成。

shlex.split使用类似于shell的语法拆分字符串cmd。

其余情况:

如果这对于某些特定命令不起作用,很可能您与命令行解释器有问题。操作系统选择了默认的解释器,但它不适合您的程序类型,或者在系统可执行路径中找不到足够的解释器。

例子:

在Unix系统上使用重定向运算符。

input_1 = "input_1.txt"
input_2 = "input_2.txt"
output = "merged.txt"
base_command = "/bin/bash -c 'cat {input} >> {output}'"

base_command.format(input_1, output=output)
subprocess.check_call(shlex.split(base_command))

base_command.format(input_2, output=output)
subprocess.check_call(shlex.split(base_command))

正如Python禅宗所述:显式优于隐式。因此,如果使用Python >=3.6函数,则应该如下所示:
import subprocess
import shlex

def run_command(cmd_interpreter: str, command: str) -> None:
    base_command = f"{cmd_interpreter} -c '{command}'"
    subprocess.check_call(shlex.split(base_command)


13

我经常使用以下函数来运行外部命令,特别适用于长时间运行的进程。下面的方法在进程正在运行时跟踪进程输出并返回输出结果,如果进程失败,则引发异常

当使用进程的poll()方法完成该进程时,它会退出。

import subprocess,sys

def exec_long_running_proc(command, args):
    cmd = "{} {}".format(command, " ".join(str(arg) if ' ' not in arg else arg.replace(' ','\ ') for arg in args))
    print(cmd)
    process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)

    # Poll process for new output until finished
    while True:
        nextline = process.stdout.readline().decode('UTF-8')
        if nextline == '' and process.poll() is not None:
            break
        sys.stdout.write(nextline)
        sys.stdout.flush()

    output = process.communicate()[0]
    exitCode = process.returncode

    if (exitCode == 0):
        return output
    else:
        raise Exception(command, exitCode, output)

你可以这样调用它:

exec_long_running_proc(command = "hive", args=["-f", hql_path])

1
将带有空格的参数传递给函数会得到意想不到的结果。使用repr(arg)而不是str(arg)可能有所帮助,因为Python和Shell以相同的方式转义引号。 - sbk
1
@sbk repr(arg) 没有真正帮助,上面的代码也处理了空格。现在以下代码可以工作 exec_long_running_proc(command = "ls", args=["-l", "~/test file*"]) - am5

12

这是我的建议:在处理外部命令时,我认为这是最佳实践...

这些是执行方法的返回值...

pass, stdout, stderr = execute(["ls","-la"],"/home/user/desktop")

这是执行方法...

def execute(cmdArray,workingDir):

    stdout = ''
    stderr = ''

    try:
        try:
            process = subprocess.Popen(cmdArray,cwd=workingDir, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=1)
        except OSError:
            return [False, '', 'ERROR : command(' + ' '.join(cmdArray) + ') could not get executed!']

        for line in iter(process.stdout.readline, b''):

            try:
                echoLine = line.decode("utf-8")
            except:
                echoLine = str(line)

            stdout += echoLine

        for line in iter(process.stderr.readline, b''):

            try:
                echoLine = line.decode("utf-8")
            except:
                echoLine = str(line)

            stderr += echoLine

    except (KeyboardInterrupt,SystemExit) as err:
        return [False,'',str(err)]

    process.stdout.close()

    returnCode = process.wait()
    if returnCode != 0 or stderr != '':
        return [False, stdout, stderr]
    else:
        return [True, stdout, stderr]

1
死锁潜在:使用.communicate方法代替。 - pppery
更好的做法是避免使用Popen(),而是使用更高级别的API,现在已经整合到单个函数subprocess.run()中。 - tripleee

11

我编写了一个小型库来帮助这种使用情况:

https://pypi.org/project/citizenshell/

它可以使用以下命令进行安装:

pip install citizenshell

然后按以下方式使用:

from citizenshell import sh
assert sh("echo Hello World") == "Hello World"
您可以按如下方式将标准输出与标准错误分离并提取退出代码:
result = sh(">&2 echo error && echo output && exit 13")
assert result.stdout() == ["output"]
assert result.stderr() == ["error"]
assert result.exit_code() == 13

而且很棒的是,您无需等待底层 shell 退出即可开始处理输出:

for line in sh("for i in 1 2 3 4; do echo -n 'It is '; date +%H:%M:%S; sleep 1; done", wait=False)
    print ">>>", line + "!"

通过wait=False,将按照可用的行打印它们

>>> It is 14:24:52!
>>> It is 14:24:53!
>>> It is 14:24:54!
>>> It is 14:24:55!

更多示例可在https://github.com/meuter/citizenshell找到。


11

只是为了补充讨论,如果您使用Python控制台,可以从IPython调用外部命令。在IPython提示符中,您可以通过前缀'!'来调用shell命令。您还可以在Python代码中与shell结合,并将shell脚本的输出分配给Python变量。

例如:

In [9]: mylist = !ls

In [10]: mylist
Out[10]:
['file1',
 'file2',
 'file3',]

10

在Python中调用外部命令

调用外部命令的简单方法是使用os.system(...)。该函数返回命令的退出值,但缺点是我们无法获取标准输出和错误输出。

ret = os.system('some_cmd.sh')
if ret != 0 :
    print 'some_cmd.sh execution returned failure'

在Python中后台调用外部命令

subprocess.Popen 提供了比使用 os.system 更多的灵活性来运行外部命令。我们可以在后台启动一个命令并等待它完成。之后,我们可以获取标准输出和标准错误输出。

proc = subprocess.Popen(["./some_cmd.sh"], stdout=subprocess.PIPE)
print 'waiting for ' + str(proc.pid)
proc.wait()
print 'some_cmd.sh execution finished'
(out, err) = proc.communicate()
print 'some_cmd.sh output : ' + out

在Python中调用长时间运行的外部命令并在一定时间后停止

我们甚至可以使用subprocess.Popen在后台启动一个长时间运行的进程,并在其任务完成后一段时间内杀死它。

proc = subprocess.Popen(["./some_long_run_cmd.sh"], stdout=subprocess.PIPE)
# Do something else
# Now some_long_run_cmd.sh exeuction is no longer needed, so kill it
os.system('kill -15 ' + str(proc.pid))
print 'Output : ' proc.communicate()[0]

9
作为示例(在Linux中):
import subprocess
subprocess.run('mkdir test.dir', shell=True)

这将在当前目录下创建test.dir文件夹。 请注意,以下方式同样有效:
import subprocess
subprocess.call('mkdir test.dir', shell=True)

使用os.system的等效代码是:

import os
os.system('mkdir test.dir')

最佳实践是使用subprocess而不是os,优先考虑.run而非.call。 关于subprocess,你需要知道的都在这里。 此外,请注意所有Python文档都可以从这里下载。我下载了打包成.zip的PDF文件。我提到这一点是因为tutorial.pdf(第81页)中有一个关于os模块的良好概述。此外,它是Python编程者的权威资源。

2
根据 https://docs.python.org/2/library/subprocess.html#frequently-used-arguments ,"shell=True" 可能会引起安全问题。 - Nick
@Nick Predley:注意到了,但是“shell=False”并不能执行所需的功能。具体有哪些安全问题以及替代方案是什么?请尽快告诉我:我不希望发布任何可能会给查看此内容的任何人带来麻烦的东西。 - user8468899
1
基本警告在文档中,但这个问题在这里更详细地解释了:https://dev59.com/lHA75IYBdhLWcg3wqK__ - tripleee

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