在Python中调用外部命令

问题描述 投票:4296回答:56

如何从Python脚本中调用外部命令(就像我在Unix shell或Windows命令提示符下键入它一样)?

python shell command subprocess external
56个回答
4148
投票

看看标准库中的subprocess module

import subprocess
subprocess.run(["ls", "-l"])

子进程与系统的优势在于它更灵活(您可以获得stdout,stderr,“真实”状态代码,更好的错误处理等等)。

official documentation推荐使用替代os.system()的子进程模块:

子流程模块提供了更强大的工具来生成新流程并检索其结果;使用该模块比使用此函数[os.system()]更可取。

子流程文档中的“Replacing Older Functions with the subprocess Module”部分可能有一些有用的配方。

旧版本的Python使用调用:

import subprocess
subprocess.call(["ls", "-l"])

63
投票

检查“pexpect”Python库。

它允许交互式控制外部程序/命令,甚至ssh,ftp,telnet等。你可以输入如下内容:

from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )

59
投票

如果您需要调用命令的输出,则可以使用sh (Python subprocess interface)(Python 2.7+)。

from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)

另请注意child = pexpect.spawn('ftp 192.168.0.24') child.expect('(?i)name .*: ') child.sendline('anonymous') child.expect('(?i)password') 参数。

如果shell是subprocess.check_output,则指定的命令将通过shell执行。如果您主要使用Python来提供它在大多数系统shell上提供的增强控制流,并且仍然希望方便地访问其他shell功能,例如shell管道,文件名通配符,环境变量扩展以及将〜扩展到用户家中,这将非常有用。目录。但请注意,Python本身提供了许多类似shell的功能(特别是>>> subprocess.check_output(["ls", "-l", "/dev/null"]) 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n' shellTrueglobfnmatchos.walk())的实现。


56
投票

With Standard Library

使用os.path.expandvars()(Python 3):

os.path.expanduser()

这是推荐的标准方式。但是,更复杂的任务(管道,输出,输入等)构造和写入可能很繁琐。

关于Python版本的注意事项:如果您仍在使用Python 2,则shutil的工作方式类似。

ProTip:subprocess module可以帮助您解析import subprocess subprocess.run(['ls', '-l']) subprocess.call和其他shlex.split函数的命令,以防您不希望(或者您不能!)以列表的形式提供它们:

run

With External Dependencies

如果您不介意外部依赖项,请使用qazxsw poi:

call

这是最好的subprocess包装。它是跨平台的,即它适用于Windows和类Unix系统。由import shlex import subprocess subprocess.run(shlex.split('ls -l')) 安装。

另一个受欢迎的图书馆是plumbum

from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())

然而,subprocess放弃了Windows支持,所以它不像过去那样棒。由pip install plumbum安装。


49
投票

这就是我运行命令的方式。此代码包含您需要的所有内容

sh

45
投票

Update:

如果您的代码不需要保持与早期Python版本的兼容性,那么from sh import ifconfig print(ifconfig('wlan0')) 是推荐的方法sh。它更加一致,并提供与Envoy类似的易用性。 (虽然管道不是那么简单。请参阅pip install sh。)

以下是from subprocess import Popen, PIPE cmd = "ls -l ~/" p = Popen(cmd , shell=True, stdout=PIPE, stderr=PIPE) out, err = p.communicate() print "Return code: ", p.returncode print out.rstrip(), err.rstrip() 的一些例子。

运行一个过程:

subprocess.run

提高失败的运行:

as of Python 3.5

捕获输出:

this question for how

Original answer:

我建议尝试the docs。它是子进程的包装器,后者反过来>>> 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"], stdout=subprocess.PIPE)
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')

管道周围的东西:

Envoy

37
投票

没有结果的输出:

aims to replace

输出结果:

the readme

32
投票

>>> r = envoy.run('git config', data='data to pipe in', timeout=2) >>> r.status_code 129 >>> r.std_out 'usage: git config [options]' >>> r.std_err ''

......或者是一个非常简单的命令:

>>> r = envoy.run('uptime | pbcopy')

>>> r.command
'pbcopy'
>>> r.status_code
0

>>> r.history
[<Response 'uptime'>]

30
投票

还有import os os.system("your command here")

import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")

29
投票

https://docs.python.org/2/library/subprocess.html还可以,但有点过时了。它也不是很安全。相反,尝试import os os.system('cat testfile') Plumbum没有直接打电话给sh,因此比>>> from plumbum import local >>> ls = local["ls"] >>> ls LocalCommand(<LocalPath /bin/ls>) >>> ls() u'build.py\ndist\ndocs\nLICENSE\nplumbum\nREADME.rst\nsetup.py\ntests\ntodo.txt\n' >>> notepad = local["c:\\windows\\notepad.exe"] >>> notepad() # Notepad window pops up u'' # Notepad window is closed by user, command returns 更安全。

获取更多信息os.system


28
投票

在Python中调用外部命令

简单,使用subprocess,它返回一个subprocess对象:

os.system

Why?

从Python 3.5开始,文档推荐使用here

调用子进程的推荐方法是对它可以处理的所有用例使用run()函数。对于更高级的用例,可以直接使用底层的Popen接口。

以下是最简单的使用示例 - 它完全按照要求执行:

subprocess.run

CompletedProcess等待命令成功完成,然后返回一个>>> import subprocess >>> completed_process = subprocess.run('python --version') Python 3.6.1 :: Anaconda 4.4.0 (64-bit) >>> completed_process CompletedProcess(args='python --version', returncode=0) 对象。它可能会提高subprocess.run(如果你给它一个>>> import subprocess >>> completed_process = subprocess.run('python --version') Python 3.6.1 :: Anaconda 4.4.0 (64-bit) >>> completed_process CompletedProcess(args='python --version', returncode=0) 参数)或run(如果它失败并且你通过CompletedProcess)。

正如您可能从上面的示例中推断的那样,默认情况下,stdout和stderr都会通过管道输出到您自己的stdout和stderr。

我们可以检查返回的对象并查看给出的命令和返回码:

TimeoutExpired

Capturing output

如果要捕获输出,可以将timeout=传递给相应的CalledProcessErrorcheck=True

>>> completed_process.args
'python --version'
>>> completed_process.returncode
0

(我觉得有趣且有点违反直觉,版本信息被放到stderr而不是stdout。)

传递命令列表

人们可以轻松地从手动提供命令字符串(如问题建议)到提供以编程方式构建的字符串。不要以编程方式构建字符串。这是一个潜在的安全问题。假设你不相信输入,那就更好了。

subprocess.PIPE

请注意,只有stderr应该通过位置传递。

完整签名

这是源代码中的实际签名,如stdout所示:

>>> cp = subprocess.run('python --version', 
                        stderr=subprocess.PIPE, 
                        stdout=subprocess.PIPE)
>>> cp.stderr
b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit)\r\n'
>>> cp.stdout
b''

>>> import textwrap >>> args = ['python', textwrap.__file__] >>> cp = subprocess.run(args, stdout=subprocess.PIPE) >>> cp.stdout b'Hello there.\r\n This is indented.\r\n' args被赋予help(run)构造函数。 def run(*popenargs, input=None, timeout=None, check=False, **kwargs): 可以是一串字节(或unicode,如果指定编码或popenargs),它将被传送到子进程的stdin。

该文档比我更好地描述了kwargsPopen

timeout参数传递给Popen.communicate()。如果超时到期,子进程将被终止并等待。子进程终止后,将重新引发TimeoutExpired异常。

如果check为true,并且进程以非零退出代码退出,则将引发CalledProcessError异常。该异常的属性包含参数,退出代码以及stdout和stderr(如果它们被捕获)。

而这个input的例子比我想出的更好:

universal_newlines=True

Expanded Signature

这是一个扩展的签名,如文档中所示:

timeout=

请注意,这表示只应按位置传递args列表。因此将剩余的参数作为关键字参数传递。

Popen

当使用check=True而不是?我很难根据论据单独找到用例。但是,直接使用check=True可以访问其方法,包括>>> subprocess.run("exit 1", shell=True, check=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1 ,'send_signal','terminate'和'wait'。

这是subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None) 中给出的Popen签名。我认为这是对信息的最精确封装(与Popen相反):

poll

但更多信息是Popen

the source

在新进程中执行子程序。在POSIX上,该类使用os.execvp() - 类似行为来执行子程序。在Windows上,该类使用Windows CreateProcess()函数。 Popen的论点如下。

理解help(Popen)上的剩余文档将留给读者练习。


2786
投票

以下是调用外部程序的方法及其优缺点的摘要:

  1. os.system("some_command with args")将命令和参数传递给系统的shell。这很好,因为您实际上可以以这种方式一次运行多个命令并设置管道和输入/输出重定向。例如: os.system("some_command < input_file | another_command > output_file") 但是,虽然这很方便,但您必须手动处理shell字符的转义,例如空格等。另一方面,这也允许您运行仅仅是shell命令而不是实际外部程序的命令。见the documentation
  2. stream = os.popen("some_command with args")将执行与os.system相同的操作,除了它为您提供了一个类似文件的对象,您可以使用它来访问该进程的标准输入/输出。还有3种其他的popen变体,它们对i / o的处理方式略有不同。如果您将所有内容都作为字符串传递,那么您的命令将传递给shell;如果你将它们作为列表传递,那么你不必担心逃避任何事情。见the documentation
  3. Popen模块的subprocess类。这可以作为os.popen的替代品,但由于如此全面,因此具有稍微复杂的缺点。例如,你会说: print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read() 代替: print os.popen("echo Hello World").read() 但是在一个统一的类而不是4个不同的popen函数中拥有所有选项是很好的。见the documentation
  4. 来自call模块的subprocess函数。这基本上就像Popen类并采用所有相同的参数,但它只是等待命令完成并给你返回代码。例如: return_code = subprocess.call("echo Hello World", shell=True) the documentation
  5. 如果您使用的是Python 3.5或更高版本,则可以使用新的subprocess.run函数,它与上面的内容非常相似,但更灵活,并在命令完成执行时返回CompletedProcess对象。
  6. os模块还具有C程序中的所有fork / exec / spawn函数,但我不建议直接使用它们。

subprocess模块可能应该是你使用的。

最后请注意,对于所有方法,您将最终命令传递给shell作为字符串执行,并且您负责转义它。如果您传递的字符串的任何部分无法完全受信任,则会产生严重的安全隐患。例如,如果用户正在输入字符串的某些/任何部分。如果您不确定,请仅将这些方法与常量一起使用。为了给您一些暗示,请考虑以下代码:

print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()

并且想象用户输入“我的妈妈不爱我&& rm -rf /”这可能会删除整个文件系统。


25
投票

使用:

def __init__(self, args, bufsize=-1, executable=None,
             stdin=None, stdout=None, stderr=None,
             preexec_fn=None, close_fds=_PLATFORM_DEFAULT_CLOSE_FDS,
             shell=False, cwd=None, env=None, universal_newlines=False,
             startupinfo=None, creationflags=0,
             restore_signals=True, start_new_session=False,
             pass_fds=(), *, encoding=None, errors=None):

os - 此模块提供了一种使用操作系统相关功能的便携方式。

对于更多the Popen documentation函数,subprocess.Popen(args, bufsize=-1, executable=None, stdin=None, stdout=None, stderr=None, preexec_fn=None, close_fds=True, shell=False, cwd=None, env=None, universal_newlines=False, startupinfo=None, creationflags=0, restore_signals=True, start_new_session=False, pass_fds=(), *, encoding=None, errors=None) 是文档。


25
投票

它可以很简单:

Popen

21
投票

使用os模块

import os

cmd = 'ls -al'

os.system(cmd)

例如

os

20
投票

如果您不想测试返回值,here很方便。它会在任何错误上抛出异常。


20
投票

我倾向于使用import os cmd = "your command" os.system(cmd) import os os.system("your command") (处理引用字符串的转义):

import os
os.system("ifconfig")

20
投票

这里有另一个不同之处,前面没有提到过。

subprocess.check_call执行 作为子进程。在我的情况下,我需要执行需要与另一个程序通信的文件。

我尝试了subprocess,执行成功了。但是<b>无法与<a>通信。当我从终端运行时,一切正常。

还有一个:(注意:kwrite的行为与其他应用程序不同。如果您使用Firefox尝试以下操作,结果将不同。)

如果你尝试subprocess,程序流冻结,直到用户关闭kwrite。为了克服这一点,我尝试了shlex。这个时间程序继续流动,但kwrite成为控制台的子进程。

任何人都运行kwrite不是一个子进程(即在系统监视器中它必须出现在树的最左边)。


19
投票

>>> import subprocess, shlex >>> command = 'ls -l "/your/path/with spaces/"' >>> call_params = shlex.split(command) >>> print call_params ["ls", "-l", "/your/path/with spaces/"] >>> subprocess.call(call_params) 不允许您存储结果,因此如果您想将结果存储在某些列表或subprocess.Popen工作的东西。


19
投票

我非常喜欢os.system("kwrite")的简单性。它建立在子进程模块之上。

以下是文档中的示例:

os.system(konsole -e kwrite)

16
投票

无耻的插件,我为此写了一个库:P os.system

它现在基本上是popen和shlex的包装器。它还支持管道命令,因此您可以在Python中更轻松地链接命令。所以你可以这样做:

subprocess.call

15
投票

在Linux下,如果你想调用一个独立执行的外部命令(将在python脚本终止后继续运行),你可以使用一个简单的队列作为shell_command>>> from shell_command import shell_call >>> shell_call("ls *.py") setup.py shell_command.py test_shell_command.py 0 >>> shell_call("ls -l *.py") -rw-r--r-- 1 ncoghlan ncoghlan 391 2011-12-11 12:07 setup.py -rw-r--r-- 1 ncoghlan ncoghlan 7855 2011-12-11 16:16 shell_command.py -rwxr-xr-x 1 ncoghlan ncoghlan 8463 2011-12-11 16:17 test_shell_command.py 0 命令

任务假脱机程序的示例:

https://github.com/houqp/shell.py

关于任务假脱机程序的注释(ex('echo hello shell.py') | "awk '{print $2}'" ):

  1. 您可以使用以下命令设置要运行的并发进程数(“slots”): task spooler
  2. 安装at不需要管理员权限。您可以使用简单的import os os.system('ts <your-command>') 从源代码下载并编译它,将其添加到您的路径中,您就完成了。

307
投票

我通常使用:

import subprocess

p = subprocess.Popen('ls', shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
for line in p.stdout.readlines():
    print line,
retval = p.wait()

您可以使用管道中的qazxsw poi数据自由地执行您想要的操作。事实上,你可以简单地省略那些参数(qazxsw poi和qazxsw poi),它的行为就像stdout


14
投票

您可以使用Popen,然后您可以检查程序的状态:

ts

看看ts -S <number-of-slots>


196
投票

关于将子进程从调用进程中分离的一些提示(在后台启动子进程)。

假设您想从CGI脚本启动一个长任务,即子进程应该比CGI脚本执行过程更长寿。

子流程模块docs的经典示例是:

stdout=

这里的想法是你不想在“调用子进程”行中等待,直到longtask.py完成。但目前尚不清楚在这个例子中“更多代码”这一行之后会发生什么。

我的目标平台是freebsd,但开发是在windows上,所以我首先在windows上遇到了问题。

在Windows(win xp)上,父进程在longtask.py完成其工作之前不会完成。这不是你想要的CGI脚本。问题不是Python特有的,在PHP社区中问题是一样的。

解决方案是将DETACHED_PROCESS stderr=传递给win API中的底层CreateProcess函数。如果你碰巧安装了pywin32,你可以从win32process模块​​导入标志,否则你应该自己定义:

os.system()

/ * UPD 2015.10.27 @eryksun在下面的评论中指出,语义正确的标志是CREATE_NEW_CONSOLE(0x00000010)* /

在freebsd上我们还有另一个问题:父进程完成后,它也会完成子进程。这也不是你想要的CGI脚本。一些实验表明问题似乎是在共享sys.stdout。工作解决方案如下:

import subprocess
import sys

# some code here

pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess

# some more code here

我没有检查其他平台上的代码,也不知道freebsd上行为的原因。如果有人知道,请分享您的想法。谷歌搜索Python中的后台进程并没有任何亮点。


120
投票

我建议使用子进程模块而不是os.system,因为它会为你进行shell转义,因此更加安全:Process Creation Flag

DETACHED_PROCESS = 0x00000008

pid = subprocess.Popen([sys.executable, "longtask.py"],
                       creationflags=DETACHED_PROCESS).pid

120
投票
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)

如果要返回命令的结果,可以使用http://docs.python.org/library/subprocess.html。但是,自2.6版本以来,这已经被弃用了subprocess.call(['ping', 'localhost']) ,其他答案已经很好地解决了。


115
投票
import os
cmd = 'ls -al'
os.system(cmd)

请注意,这是危险的,因为未清除该命令。我把它留给你去谷歌搜索关于'os'和'sys'模块的相关文档。有一堆函数(exec *和spawn *)会做类似的事情。


72
投票

有许多不同的库允许您使用Python调用外部命令。对于每个库,我已经给出了描述并显示了调用外部命令的示例。我用作示例的命令是os.popen(列出所有文件)。如果您想了解有关我列出的任何库的更多信息,并链接每个库的文档。

资料来源:

这些都是图书馆:

希望这可以帮助您决定使用哪个库:)

子进程允许您调用外部命令并将它们连接到它们的输入/输出/错误管道(stdin,stdout和stderr)。子进程是运行命令的默认选择,但有时其他模块更好。

http://www.fabfile.org/

os用于“依赖于操作系统的功能”。它还可以用于使用https://github.com/kennethreitz/envoyhttps://docs.python.org/2/library/commands.html调用外部命令(注意:还有一个subprocess.popen)。操作系统将始终运行shell,对于那些不需要或不知道如何使用subprocess.run(["ls", "-l"]) # Run command subprocess.run(["ls", "-l"], stdout=subprocess.PIPE) # This will run the command and return any output subprocess.run(shlex.split("ls -l")) # You can also use the shlex library to split the command 的人来说,它是一个简单的选择。

os.system

SH

sh是一个子进程接口,它允许您像调用函数一样调用程序。如果要多次运行命令,这非常有用。

os.popen

带领

plumbum是一个用于“类似脚本”的Python程序的库。您可以像subprocess.run一样调用类似函数的程序。如果要运行没有shell的管道,Plumbum很有用。

os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output

Pexpect的

pexpect允许您生成子应用程序,控制它们并在其输出中查找模式。对于期望在Unix上使用tty的命令,这是替代子进程的更好的替代方法。

sh.ls("-l") # Run command normally
ls_cmd = sh.Command("ls") # Save command as a variable
ls_cmd() # Run command as if it were a function

fabric是一个Python 2.5和2.7库。它允许您执行本地和远程shell命令。 Fabric是在安全shell(SSH)中运行命令的简单替代方法

sh

使者

特使被称为“人类的子过程”。它被用作围绕ls_cmd = plumbum.local("ls -l") # get command ls_cmd() # run command 模块的便利包装器。

pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo [email protected]:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')

命令

fabric.operations.local('ls -l') # Run command as normal fabric.operations.local('ls -l', capture = True) # Run command and receive output 包含subprocess的包装函数,但它已从Python 3中删除,因为r = envoy.run("ls -l") # Run command r.std_out # get output 是一个更好的选择。

该编辑基于J.F. Sebastian的评论。


64
投票

我总是使用commands这样的事情:

os.popen

但这似乎是一个很好的工具:subprocess

看一个例子:

fabric
© www.soinside.com 2019 - 2024. All rights reserved.