您如何在Python脚本中调用外部命令(就像我在Unix Shell或Windows命令提示符下键入的一样)?
#1楼
os.system
不允许您存储结果,因此,如果您要将结果存储在某个列表中或subprocess.call
os.system
则可以进行os.system
。
#2楼
您可以使用Popen,然后可以检查过程的状态:
from subprocess import Popen
proc = Popen(['ls', '-l'])
if proc.poll() is None:
proc.kill()
签出subprocess.Popen 。
#3楼
我非常喜欢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
#4楼
这就是我运行命令的方式。 这段代码包含了您非常需要的所有内容
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()
#5楼
更新:
如果您的代码不需要维护与早期Python版本的兼容性,那么从Python 3.5开始, subprocess.run
是推荐的方法。 它更加一致,并且提供与Envoy类似的易用性。 (管道并不是那么简单。有关如何查看此问题的信息 。)
这是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 。 它是子流程的包装,后者旨在替换较旧的模块和功能。 特使是人类的子过程。
自述文件中的示例用法:
>>> 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'>]
#6楼
带有标准库
Use 子流程模块 (Python 3):
import subprocess
subprocess.run(['ls', '-l'])
这是推荐的标准方式。 但是,构造和编写更复杂的任务(管道,输出,输入等)可能很繁琐。
关于Python版本的注意事项:如果您仍在使用Python 2, subprocess.call的工作方式与此类似。
ProTip: shlex.split可以帮助您解析run
, call
和其他subprocess
函数的命令,以防您不想(或您不能!)以列表形式提供它们:
import shlex
import subprocess
subprocess.run(shlex.split('ls -l'))
具有外部依赖性
如果您不介意外部依赖性,请使用plumbum :
from plumbum.cmd import ifconfig
print(ifconfig['wlan0']())
这是最好的subprocess
包装器。 它是跨平台的,即可以在Windows和类似Unix的系统上运行。 通过pip install plumbum
。
另一个受欢迎的图书馆是sh :
from sh import ifconfig
print(ifconfig('wlan0'))
然而, sh
下降Windows的支持,所以它不是真棒,因为它曾经是。 通过pip install sh
。
#7楼
没有结果的输出:
import os
os.system("your command here")
输出结果:
import commands
commands.getoutput("your command here")
or
commands.getstatusoutput("your command here")
#8楼
这里还有另一个以前没有提到的区别。
subprocess.Popen
将<command>作为subprocess.Popen
执行。 就我而言,我需要执行文件<a>,该文件需要与另一个程序<b>通信。
我尝试了子流程,执行成功。 但是<b>无法与<a>通信。 当我从终端运行时,一切都正常。
还有一个:(注意:kwrite的行为与其他应用程序不同。如果在Firefox上尝试以下操作,结果将有所不同。)
如果尝试os.system("kwrite")
,程序流将冻结,直到用户关闭kwrite。 为了克服这个问题,我尝试改用os.system(konsole -e kwrite)
。 这个时间程序继续进行,但是kwrite成为了控制台的子进程。
任何人都运行kwrite而不是子进程(即,它必须在系统监视器中显示在树的最左侧)。
#9楼
关于从调用者中分离子进程的一些提示(在后台启动子进程)。
假设您要从CGI脚本开始一个长任务,即子进程的寿命应比CGI脚本执行过程的寿命长。
子流程模块docs中的经典示例是:
import subprocess
import sys
# some code here
pid = subprocess.Popen([sys.executable, "longtask.py"]) # call subprocess
# some more code here
这里的想法是,您不希望在long任务行中完成之前在“调用子进程”行中等待。 但是尚不清楚示例中“这里有更多代码”行之后会发生什么。
我的目标平台是freebsd,但是开发是在Windows上进行的,因此我首先在Windows上遇到了问题。
在Windows(win xp)上,直到longtask.py完成工作后,父进程才会完成。 这不是您想要的CGI脚本。 问题并非特定于Python,在PHP社区中,问题是相同的。
解决方案是将DETACHED_PROCESS 流程创建标志传递给Win API中的基础CreateProcess函数。 如果碰巧安装了pywin32,则可以从win32process模块中导入该标志,否则,您应该自己定义它:
DETACHED_PROCESS = 0x00000008
pid = subprocess.Popen([sys.executable, "longtask.py"],
creationflags=DETACHED_PROCESS).pid
/ * UPD 2015.10.27 @eryksun在下面的注释中指出,语义正确的标志是CREATE_NEW_CONSOLE(0x00000010)* /
在freebsd上,我们还有另一个问题:父进程完成时,它也会完成子进程。 那也不是您在CGI脚本中想要的。 一些实验表明,问题似乎出在共享sys.stdout。 可行的解决方案如下:
pid = subprocess.Popen([sys.executable, "longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE)
我还没有检查其他平台上的代码,也不知道freebsd上该行为的原因。 如果有人知道,请分享您的想法。 谷歌搜索在Python中启动后台进程尚未阐明。
#10楼
我倾向于将子 进程与shlex一起使用(以处理带引号的字符串的转义):
>>> 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)
#11楼
无耻的插件,我为此写了一个库:P https://github.com/houqp/shell.py
目前,它基本上是popen和shlex的包装。 它还支持管道命令,因此您可以在Python中更轻松地链接命令。 因此,您可以执行以下操作:
ex('echo hello shell.py') | "awk '{print $2}'"
#12楼
还有铅
>>> 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
#13楼
使用:
import os
cmd = 'ls -al'
os.system(cmd)
os-此模块提供了使用依赖于操作系统的功能的可移植方式。
对于更多的os
的功能, 这里是文档。
#14楼
在Windows中,您只需导入subprocess
模块并通过调用subprocess.Popen()
, subprocess.Popen().communicate()
和subprocess.Popen().wait()
来运行外部命令,如下所示:
# Python script to run a command line
import subprocess
def execute(cmd):
"""
Purpose : To execute a command and return exit status
Argument : cmd - command to execute
Return : exit_code
"""
process = subprocess.Popen(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
(result, error) = process.communicate()
rc = process.wait()
if rc != 0:
print "Error: failed to execute command:", cmd
print error
return result
# def
command = "tasklist | grep python"
print "This process detail: \n", execute(command)
输出:
This process detail:
python.exe 604 RDP-Tcp#0 4 5,660 K
#15楼
还要检查“ pexpect” Python库。
它允许交互式控制外部程序/命令,甚至包括ssh,ftp,telnet等。您可以键入以下内容:
child = pexpect.spawn('ftp 192.168.0.24')
child.expect('(?i)name .*: ')
child.sendline('anonymous')
child.expect('(?i)password')
#16楼
有很多不同的库,可让您使用Python调用外部命令。 对于每个库,我都进行了描述并显示了调用外部命令的示例。 我用作示例的命令是ls -l
(列出所有文件)。 如果您想了解有关任何库的更多信息,我已列出并链接了每个库的文档。
资料来源:
- 子流程: https : //docs.python.org/3.5/library/subprocess.html
- shlex: https ://docs.python.org/3/library/shlex.html
- 操作系统: https : //docs.python.org/3.5/library/os.html
- sh: https : //amoffat.github.io/sh/
- 铅: https : //plumbum.readthedocs.io/en/latest/
- pexpect: https ://pexpect.readthedocs.io/en/stable/
- 面料: http : //www.fabfile.org/
- 特使: https : //github.com/kennethreitz/envoy
- 命令: https : //docs.python.org/2/library/commands.html
这些都是库:
希望这可以帮助您决定使用哪个库:)
子过程
子进程允许您调用外部命令,并将它们连接到它们的输入/输出/错误管道(stdin,stdout和stderr)。 子进程是运行命令的默认选择,但有时其他模块更好。
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用于“取决于操作系统的功能”。 它也可以用于通过os.system
和os.popen
调用外部命令(注意:还有一个os.popen
)。 os将始终运行该外壳程序,对于不需要或不知道如何使用subprocess.run
人来说,它是一个简单的选择。
os.system("ls -l") # run command
os.popen("ls -l").read() # This will run the command and return any output
SH
sh是一个子进程接口,可让您像调用程序一样调用程序。 如果要多次运行命令,这很有用。
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
铅
plumbum是用于“类似脚本”的Python程序的库。 您可以像sh
函数那样调用程序。 如果您要在没有外壳的情况下运行管道,则Plumbum非常有用。
ls_cmd = plumbum.local("ls -l") # get command
ls_cmd() # run command
期待
pexpect使您可以生成子应用程序,对其进行控制并在其输出中找到模式。 对于在Unix上需要tty的命令,这是子过程的更好选择。
pexpect.run("ls -l") # Run command as normal
child = pexpect.spawn('scp foo user@example.com:.') # Spawns child application
child.expect('Password:') # When this is the output
child.sendline('mypassword')
布
fabric是一个Python 2.5和2.7库。 它允许您执行本地和远程Shell命令。 Fabric是在安全Shell(SSH)中运行命令的简单替代方案
fabric.operations.local('ls -l') # Run command as normal
fabric.operations.local('ls -l', capture = True) # Run command and receive output
使者
特使被称为“人类子过程”。 它用作subprocess
模块周围的便利包装。
r = envoy.run("ls -l") # Run command
r.std_out # get output
命令
commands
包含os.popen
包装函数,但由于subprocess
是更好的选择,因此已从Python 3中删除了该函数。
该编辑基于JF Sebastian的评论。
#17楼
在Linux下,如果您想调用将独立执行的外部命令(在python脚本终止后将继续运行),则可以使用简单的队列作为任务假脱机程序或at命令
任务假脱机程序的示例:
import os
os.system('ts <your-command>')
有关任务后台处理程序( ts
)的注意事项:
您可以使用以下命令设置要运行的并发进程数(“插槽”):
ts -S <number-of-slots>
安装
ts
不需要管理员权限。 您可以使用简单的make
从源代码下载并编译它,然后将其添加到路径中,即可完成操作。
#18楼
在Python中调用外部命令
简单,使用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)
为什么?
从Python 3.5开始,文档建议使用subprocess.run :
推荐的调用子流程的方法是将run()函数用于它可以处理的所有用例。 对于更高级的用例,可以直接使用基础Popen接口。
这是最简单的用法示例-完全按照要求进行:
>>> 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
对象。 它可能改为引发TimeoutExpired
(如果给它提供timeout=
参数)或CalledProcessError
(如果失败,并通过check=True
)。
从上面的示例可以推断,默认情况下,stdout和stderr都通过管道传递到您自己的stdout和stderr。
我们可以检查返回的对象,并查看给出的命令和返回码:
>>> completed_process.args
'python --version'
>>> completed_process.returncode
0
捕获输出
如果要捕获输出,可以将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''
(我发现将版本信息放入stderr而不是stdout很有意思,并且有点违反直觉。)
传递命令清单
可以很容易地从手动提供命令字符串(如问题所提示)转变为以编程方式构建的字符串。 不要以编程方式构建字符串。 这是一个潜在的安全问题。 最好假设您不信任输入。
>>> 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):
该popenargs
和kwargs
都送给了Popen
构造。 input
可以是将通过管道传递到子流程的stdin的字节字符串(或unicode,如果指定了encoding或universal_newlines=True
)。
该文档比我能更好地描述timeout=
和check=True
:
超时参数传递给Popen.communicate()。 如果超时到期,子进程将被终止并等待。 子进程终止后,将重新引发TimeoutExpired异常。
如果check为true,并且进程以非零退出代码退出,则将引发CalledProcessError异常。 该异常的属性包含参数,退出代码以及stdout和stderr(如果已捕获)。
这个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
扩展签名
这是文档中提供的扩展签名:
subprocess.run(args, *, stdin=None, input=None, stdout=None, stderr=None, shell=False, cwd=None, timeout=None, check=False, encoding=None, errors=None)
请注意,这表明只应在位置传递args列表。 因此,将其余参数作为关键字参数传递。
开普
何时使用Popen
代替? 我将很难仅根据参数来找到用例。 但是,直接使用Popen
可以使您访问其方法,包括poll
,'send_signal','terminate'和'wait'。
这是源代码中给出的Popen
签名。 我认为这是信息的最精确封装(与help(Popen)
相对):
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):
但是更有用的是Popen
文档 :
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)
在新进程中执行子程序。 在POSIX上,该类使用类似于os.execvp()的行为来执行子程序。 在Windows上,该类使用Windows CreateProcess()函数。 Popen的参数如下。
读者将继续了解有关Popen
的其余文档。
#19楼
如果您不想测试返回值, subprocess.check_call
很方便。 任何错误都会引发异常。
#20楼
可能很简单:
import os
cmd = "your command"
os.system(cmd)
#21楼
使用os模块
import os
os.system("your command")
例如
import os
os.system("ifconfig")
#22楼
如果您需要从您所呼叫的命令的输出,那么你可以使用subprocess.check_output (Python的2.7+)。
>>> subprocess.check_output(["ls", "-l", "/dev/null"])
'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null\n'
还要注意shell参数。
如果shell为
True
,则将通过shell执行指定的命令。 如果您主要将Python用于大多数系统外壳程序提供的增强控制流,并且仍希望方便地访问其他外壳程序功能(例如外壳程序管道,文件名通配符,环境变量扩展以及〜扩展到用户的家),则这很有用。目录。 但是,请注意,Python本身提供了许多类似shell的功能的实现(特别是glob
,fnmatch
,os.walk()
,os.path.expandvars()
,os.path.expanduser()
和shutil
)。
#23楼
import os
os.system("your command")
请注意,这很危险,因为未清除命令。 我留给你去谷歌搜索有关“操作系统”和“系统”模块的相关文档。 有很多函数(exec *和spawn *)将执行类似的操作。
#24楼
import os
cmd = 'ls -al'
os.system(cmd)
如果要返回命令的结果,可以使用os.popen
。 但是,自2.6版以来,不推荐使用此方法,而支持subprocess模块 ,其他答案已经很好地涵盖了这一点。
#25楼
查看标准库中的子流程模块:
import subprocess
subprocess.run(["ls", "-l"])
与system
相比, subprocess
的优势在于它更灵活(您可以获取stdout
, stderr
,“真实”状态代码,更好的错误处理等)。
官方文档建议使用替代os.system()
的subprocess
模块:
subprocess
模块提供了更强大的功能来生成新流程并检索其结果。 使用该模块优于使用此功能[os.system()
]。
子 subprocess
文档中的用子流程模块替换较早的功能部分可能有一些有用的方法。
对于3.5之前的Python版本,请使用call
:
import subprocess
subprocess.call(["ls", "-l"])
#26楼
我建议您使用subprocess模块而不是os.system,因为它确实为您提供外壳转义,因此更加安全: http ://docs.python.org/library/subprocess.html
subprocess.call(['ping', 'localhost'])
#27楼
https://docs.python.org/2/library/subprocess.html
...或一个非常简单的命令:
import os
os.system('cat testfile')
#28楼
os.system
可以,但是有点陈旧。 这也不是很安全。 相反,请尝试subprocess
。 subprocess
os.system
不会直接调用sh,因此比os.system
更安全。
在此处获取更多信息。
#29楼
下面总结了调用外部程序的方法以及每种方法的优缺点:
os.system("some_command with args")
将命令和参数传递到系统的外壳程序。 很好,因为您实际上可以以这种方式一次运行多个命令,并设置管道和输入/输出重定向。 例如:os.system("some_command < input_file | another_command > output_file")
但是,尽管这很方便,但是您必须手动处理外壳字符(如空格等)的转义。另一方面,这还允许您运行仅是外壳命令而不是外部程序的命令。 请参阅文档 。
stream = os.popen("some_command with args")
与os.system
的作用相同,只是它为您提供了一个类似于文件的对象,您可以使用该对象访问该进程的标准输入/输出。 Popen还有其他3种变体,它们对I / O的处理略有不同。 如果您将所有内容都作为字符串传递,那么您的命令将传递到外壳程序; 如果将它们作为列表传递,则无需担心转义任何内容。 请参阅文档 。subprocess
模块的Popen
类。 它打算替代os.popen
但缺点是由于过于全面,因此稍微复杂一些。 例如,您会说:print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read()
代替:
print os.popen("echo Hello World").read()
但是将所有选项都放在一个统一的类中而不是4个不同的popen函数是一个很好的选择。 请参阅文档 。
来自
subprocess
模块的call
函数。 基本上,这和Popen
类一样,并采用所有相同的参数,但是它只是等待命令完成并提供返回代码。 例如:return_code = subprocess.call("echo Hello World", shell=True)
请参阅文档 。
如果您使用的是Python 3.5或更高版本,则可以使用新的
subprocess.run
函数,该函数与上面的代码非常相似,但是更加灵活,并在命令完成执行后返回CompletedProcess
对象。os模块还具有您在C程序中拥有的所有fork / exec / spawn函数,但是我不建议直接使用它们。
subprocess
模块可能应该是您所使用的模块。
最后,请注意,对于所有方法,在这些方法中,您将要由外壳执行的最终命令作为字符串传递给您,并且您有责任对其进行转义。 如果您传递的字符串的任何部分不能被完全信任,则将带来严重的安全隐患 。 例如,如果用户正在输入字符串的某些/任何部分。 如果不确定,请仅将这些方法与常量一起使用。 为了给您暗示的含义,请考虑以下代码:
print subprocess.Popen("echo %s " % user_input, stdout=PIPE).stdout.read()
并想象用户输入了“我的妈妈不爱我&& rm -rf /”,这可能会擦除整个文件系统。
#30楼
典型的实现:
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()
您可以随意使用管道中的stdout
数据执行所需的操作。 实际上,您可以简单地忽略这些参数( stdout=
和stderr=
),其行为类似于os.system()
。
#31楼
我总是将fabric
用于此类事情:
from fabric.operations import local
result = local('ls', capture=True)
print "Content:/n%s" % (result, )
但这似乎是一个很好的工具: sh
(Python子进程接口) 。
看一个例子:
from sh import vgdisplay
print vgdisplay()
print vgdisplay('-v')
print vgdisplay(v=True)