如何从Python脚本中调用外部命令(就像我在Unix shell或Windows命令提示符中输入它一样)?
看看标准库中的子进程模块:
1 2 | import subprocess subprocess.run(["ls","-l"]) |
子进程相对于系统的优势在于它更加灵活(您可以获得stdout、stderr、"实际"状态代码、更好的错误处理等等)。
官方文件建议在os.system()上使用子流程模块:
The subprocess module provides more powerful facilities for spawning new processes and retrieving their results; using that module is preferable to using this function [
os.system() ].
子流程文档中的"用子流程模块替换旧函数"部分可能有一些有用的方法。
较老版本的Python使用调用:
1 2 | import subprocess subprocess.call(["ls","-l"]) |
下面是调用外部程序的方法和每种方法的优缺点的总结:
1 | os.system("some_command < input_file | another_command > output_file") |
然而,虽然这很方便,但是您必须手动处理shell字符的转义,比如空格等。另一方面,这也允许您运行简单的shell命令,而不是实际的外部程序。见文档。
1 | print subprocess.Popen("echo Hello World", shell=True, stdout=subprocess.PIPE).stdout.read() |
而不是:
1 | print os.popen("echo Hello World").read() |
但是把所有的选项放在一个统一的类中而不是四个不同的popen函数中是很好的。见文档。
来自
1 | return_code = subprocess.call("echo Hello World", shell=True) |
见文档。
如果您使用的是Python 3.5或更高版本,您可以使用新的
os模块还具有C程序中所有的fork/exec/spawn函数,但我不建议直接使用它们。
最后,请注意,对于所有传递shell将作为字符串执行的最终命令的方法,您都要负责转义它。如果传递的字符串的任何部分不能完全信任,就会产生严重的安全问题。例如,如果用户正在输入字符串的某些/任何部分。如果您不确定,请仅对常量使用这些方法。为了给你一个暗示,考虑以下代码:
1 | print subprocess.Popen("echo %s" % user_input, stdout=PIPE).stdout.read() |
想象一下,用户输入"我妈妈不爱我"。rm射频/"。
我通常使用:
1 2 3 4 5 6 | 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() |
您可以对管道中的
关于从调用进程中分离子进程的一些提示(在后台启动子进程)。
假设您想从cgi脚本启动一个长任务,即子进程应该比cgi脚本执行进程活得更长。
子流程模块文档中的经典例子是:
1 2 3 4 5 6 7 8 | import subprocess import sys # some code here pid = subprocess.Popen([sys.executable,"longtask.py"]) # call subprocess # some more code here |
这里的思想是,您不希望在"调用子进程"行中等待,直到longtask.py完成。但是不清楚在示例中的"这里还有一些代码"行之后会发生什么。
我的目标平台是freebsd,但是开发是在windows上进行的,所以我首先在windows上遇到了这个问题。
在windows (win xp)上,父进程在longtask.py完成它的工作之前不会完成。这不是您在cgi脚本中想要的。这个问题不是特定于Python的,在PHP社区中也是一样的。
解决方案是将DETACHED_PROCESS进程创建标志传递给win API中的底层CreateProcess函数。如果你恰好安装了pywin32,你可以从win32process模块导入这个标志,否则你应该自己定义它:
1 2 3 4 | 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。有效的解决方案如下:
1 | pid = subprocess.Popen([sys.executable,"longtask.py"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) |
我没有在其他平台上检查过代码,也不知道freebsd上行为的原因。如果有人知道,请分享你的想法。在Python中搜索启动后台进程还没有给出任何答案。
1 2 3 | import os cmd = 'ls -al' os.system(cmd) |
如果希望返回命令的结果,可以使用
我建议使用子进程模块而不是os。因为它为您提供了shell转义,因此更加安全:http://docs.python.org/library/subprocess.html
1 | subprocess.call(['ping', 'localhost']) |
1 2 | import os os.system("your command") |
注意,这很危险,因为命令没有被清除。关于"os"和"sys"模块的相关文档,我将留给您谷歌。有很多函数(exec*和spawn*)可以做类似的事情。
有许多不同的库允许您使用Python调用外部命令。对于每个库,我都给出了一个描述,并展示了调用外部命令的示例。我用作示例的命令是
Sources:
子流程:https://docs.python.org/3.5/library/subprocess.htmlshlex: https://docs.python.org/3/library/shlex.html操作系统:https://docs.python.org/3.5/library/os.html承宪: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
These are all the libraries:
希望这将帮助您决定使用哪个库:)
subprocess
子进程允许您调用外部命令,并将它们连接到它们的输入/输出/错误管道(stdin、stdout和stderr)。子进程是运行命令的默认选择,但有时其他模块更好。
1 2 3 | 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用于"操作系统相关功能"。它还可以用来调用带有
1 2 | os.system("ls -l") # run command os.popen("ls -l").read() # This will run the command and return any output |
sh
sh是一个子进程接口,它允许您像调用函数一样调用程序。如果您希望多次运行命令,这将非常有用。
1 2 3 | 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
plumbum是一个用于"类脚本"Python程序的库。您可以像在
1 2 | ls_cmd = plumbum.local("ls -l") # get command ls_cmd() # run command |
pexpect
pexpect允许派生子应用程序,控制它们并在它们的输出中找到模式。对于希望在Unix上使用tty的命令,这是子进程的更好选择。
1 2 3 4 | 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
fabric是一个Python 2.5和2.7库。它允许您执行本地和远程shell命令。Fabric是在安全shell (SSH)中运行命令的简单替代方法
1 2 | fabric.operations.local('ls -l') # Run command as normal fabric.operations.local('ls -l', capture = True) # Run command and receive output |
envoy
特使被称为"人类的子进程"。它被用作
1 2 | r = envoy.run("ls -l") # Run command r.std_out # get output |
commands
编辑是基于j·f·塞巴斯蒂安的评论。
我总是使用
1 2 3 | from fabric.operations import local result = local('ls', capture=True) print"Content:/n%s" % (result, ) |
但这似乎是一个很好的工具:
看一个例子:
1 2 3 4 | from sh import vgdisplay print vgdisplay() print vgdisplay('-v') print vgdisplay(v=True) |
也可以检查"pexpect"Python库。
它允许交互控制外部程序/命令,甚至ssh, ftp, telnet等。你可以这样输入:
1 2 3 4 5 6 7 | child = pexpect.spawn('ftp 192.168.0.24') child.expect('(?i)name .*: ') child.sendline('anonymous') child.expect('(?i)password') |
如果需要调用的命令的输出,然后可以使用子进程。check_output (Python 2.7 +)。
1 2 3 | >>> subprocess.check_output(["ls","-l","/dev/null"]) 'crw-rw-rw- 1 root root 1, 3 Oct 18 2007 /dev/null ' |
还要注意shell参数。
If shell is
True , the specified command will be executed through the shell. This can be useful if you are using Python primarily for the enhanced control flow it offers over most system shells and still want convenient access to other shell features such as shell pipes, filename wildcards, environment variable expansion, and expansion of ~ to a user’s home directory. However, note that Python itself offers implementations of many shell-like features (in particular,glob ,fnmatch ,os.walk() ,os.path.expandvars() ,os.path.expanduser() , andshutil ).
与标准库
的使用子进程模块(python3):
1 2 | import subprocess subprocess.run(['ls', '-l']) |
这是推荐的标准方法。然而,更复杂的任务(管道、输出、输入等)的构造和编写可能非常单调乏味。
关于Python版本的注意事项:如果您仍然在使用python2,子进程。call的工作原理类似。
ProTip: shlex。split可以帮助您解析
1 2 3 | import shlex import subprocess subprocess.run(shlex.split('ls -l')) |
与外部依赖关系
如果您不介意外部依赖,请使用铅:
1 2 | from plumbum.cmd import ifconfig print(ifconfig['wlan0']()) |
它是最好的
另一个受欢迎的图书馆是sh:
1 2 | from sh import ifconfig print(ifconfig('wlan0')) |
但是,
这就是我运行命令的方式。这段代码包含了您需要的所有内容
1 2 3 4 5 6 | 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() |
更新:
如果您的代码不需要维护与早期Python版本的兼容性,那么从Python 3.5开始推荐使用
下面是文档中的一些例子。
运行流程:
1 2 | >>> subprocess.run(["ls","-l"]) # doesn't capture output CompletedProcess(args=['ls', '-l'], returncode=0) |
失败运行时的提升:
1 2 3 4 | >>> subprocess.run("exit 1", shell=True, check=True) Traceback (most recent call last): ... subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1 |
捕获输出:
1 2 3 4 | >>> 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 ') |
原始答:
我建议试试特使。它是子进程的包装器,子进程的目标是替换旧的模块和函数。特使是人类的子进程。
自述文件中的示例用法:
1 2 3 4 5 6 7 8 | >>> 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 '' |
管道周围的东西:
1 2 3 4 5 6 7 8 9 | >>> r = envoy.run('uptime | pbcopy') >>> r.command 'pbcopy' >>> r.status_code 0 >>> r.history [<Response 'uptime'>] |
不输出结果:
1 2 | import os os.system("your command here") |
输出结果:
1 2 3 4 | import commands commands.getoutput("your command here") or commands.getstatusoutput("your command here") |
https://docs.python.org/2/library/subprocess.html
…或者一个非常简单的命令:
1 2 | import os os.system('cat testfile') |
还有铅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | >>> from plumbum import local >>> ls = local["ls"] >>> ls LocalCommand(<LocalPath /bin/ls>) >>> ls() u'build.py dist docs LICENSE plumbum README.rst setup.py tests todo.txt ' >>> notepad = local["c:\\windows\ otepad.exe"] >>> notepad() # Notepad window pops up u'' # Notepad window is closed by user, command returns |
更多信息请点击这里。
Calling an external command in Python
很简单,使用
1 2 3 4 5 | >>> 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开始,文档推荐子进程。
The recommended approach to invoking subprocesses is to use the run() function for all use cases it can handle. For more advanced use cases, the underlying Popen interface can be used directly.
下面是一个最简单的用法的例子——它完全按照要求做了:
1 2 3 4 5 | >>> 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) |
从上面的示例可以推断,默认情况下,stdout和stderr都被管道连接到您自己的stdout和stderr。
我们可以检查返回的对象,并看到所给出的命令和返回码:
1 2 3 4 | >>> completed_process.args 'python --version' >>> completed_process.returncode 0 |
捕获输出
如果想捕获输出,可以将
1 2 3 4 5 6 7 8 9 | >>> cp = subprocess.run('python --version', stderr=subprocess.PIPE, stdout=subprocess.PIPE) >>> cp.stderr b'Python 3.6.1 :: Anaconda 4.4.0 (64-bit) ' >>> cp.stdout b'' |
(我发现把版本信息放到stderr而不是stdout中很有趣,而且有点违反直觉。)
传递一个命令列表可以很容易地从手动提供命令字符串(如问题所示)过渡到通过编程构建字符串。不要以编程方式构建字符串。这是一个潜在的安全问题。最好假设您不信任输入。
1 2 3 4 5 6 7 8 9 | >>> import textwrap >>> args = ['python', textwrap.__file__] >>> cp = subprocess.run(args, stdout=subprocess.PIPE) >>> cp.stdout b'Hello there. This is indented. ' |
注意,只有
完整签名
下面是源代码中的实际签名,如
1 def run(*popenargs, input=None, timeout=None, check=False, **kwargs):
文件比我能更好地描述
The timeout argument is passed to Popen.communicate(). If the timeout
expires, the child process will be killed and waited for. The
TimeoutExpired exception will be re-raised after the child process has
terminated.If check is true, and the process exits with a non-zero exit code, a
CalledProcessError exception will be raised. Attributes of that
exception hold the arguments, the exit code, and stdout and stderr if
they were captured.
这个关于
1
2
3
4 >>> subprocess.run("exit 1", shell=True, check=True)
Traceback (most recent call last):
...
subprocess.CalledProcessError: Command 'exit 1' returned non-zero exit status 1
扩展签名
下面是一个扩展的签名,如文档所示:
1
2
3 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
当使用
下面是源代码中给出的
1 2 3 4 5 6 7 | 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): |
但是
1
2
3
4
5 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)Execute a child program in a new process. On POSIX, the class uses
os.execvp()-like behavior to execute the child program. On Windows,
the class uses the Windows CreateProcess() function. The arguments to
Popen are as follows.
理解关于
使用:
1 2 3 4 5 | import os cmd = 'ls -al' os.system(cmd) |
这个模块提供了一种使用操作系统相关功能的可移植方法。
对于更多的
事情可以这么简单:
1 2 3 | import os cmd ="your command" os.system(cmd) |
我倾向于使用子进程与shlex(处理转义的引用字符串):
1 2 3 4 5 6 | >>> 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) |
如果您不想测试返回值,则可以使用
这里还有一个以前没有提到的不同之处。
我尝试了子流程,并且执行成功。但是无法沟通。当我从终端运行这两个程序时,一切都是正常的。
一个:(注意:kwrite的行为与其他应用程序不同。如果你用火狐浏览器试试下面的功能,结果会不一样。)
如果您尝试
任何运行kwrite的人都不是子进程(例如,在系统监视器中,它必须出现在树的最左边)。
使用os模块
1 2 | import os os.system("your command") |
如
1 2 | import os os.system("ifconfig") |
我非常喜欢shell_command,因为它很简单。它构建在子流程模块之上。
下面是文档中的一个例子:
1 2 3 4 5 6 7 8 9 | >>> 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 |
无耻的插件,我为这个写了一个库:Phttps://github.com/houqp/shell.py
现在它基本上是popen和shlex的包装。它还支持管道命令,因此您可以在Python中更容易地链接命令。你可以这样做:
1 | ex('echo hello shell.py') |"awk '{print $2}'" |
在Linux下,如果您想调用一个独立执行的外部命令(在python脚本终止后继续运行),您可以使用一个简单的队列作为任务假脱机程序或at命令
任务假脱机程序的一个例子:
1 2 | import os os.system('ts <your-command>') |
关于任务假脱机程序的说明(
您可以使用以下命令设置要运行的并发进程数量("slot"):
安装
在Windows中,你只需要导入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | # 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: ", execute(command) |
输出:
1 2 | This process detail: python.exe 604 RDP-Tcp#0 4 5,660 K |
你可以使用Popen,然后你可以检查程序的状态:
1 2 3 4 5 | from subprocess import Popen proc = Popen(['ls', '-l']) if proc.poll() is None: proc.kill() |
查看subprocess.Popen。
从openstack中子中提取网络id:
1 2 3 4 5 6 7 | #!/usr/bin/python import os netid="nova net-list | awk '/ External / { print $2 }'" temp=os.popen(netid).read() /* here temp also contains new line ( ) */ networkId=temp.rstrip() print(networkId) |
新星网络列表的输出
1 2 3 4 5 6 7 8 | +--------------------------------------+------------+------+ | 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 | +--------------------------------------+------------+------+ |
的输出打印(networkId)
1 | 27a74fcd-37c0-4789-9414-9531b7e3f126 |
以下是我的观点:在我看来,这是处理外部命令的最佳实践……
这些是execute方法的返回值…
1 | pass, stdout, stderr = execute(["ls","-la"],"/home/user/desktop") |
这是执行方法…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | 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 2 3 4 5 6 | from commands import getstatusoutput try: return getstatusoutput("ls -ltr") except Exception, e: return None |
调用是一个Python(2.7和3.4+)任务执行工具&图书馆。它为运行shell命令提供了一个干净的高级API
1 2 3 4 5 6 7 | >>> from invoke import run >>> cmd ="pip install -r requirements.txt" >>> result = run(cmd, hide=True, warn=True) >>> print(result.ok) True >>> print(result.stdout.splitlines()[-1]) Successfully installed invocations-0.13.0 pep8-1.5.7 spec-1.3.1 |
通常,我对外部命令使用以下函数,这对于长时间运行的进程尤其方便。下面的方法在进程运行时跟踪进程输出并返回输出,如果进程失败则引发异常。
如果进程是使用进程上的poll()方法完成的,则会出现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | 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) |
您可以这样调用它:
1 | exec_long_running_proc(command ="hive", args=["-f", hql_path]) |
这里调用一个外部命令并返回或打印命令的输出:
Python子进程check_output非常适合
Run command with arguments and return its output as a byte string.
1 2 3 | import subprocess proc = subprocess.check_output('ipconfig /all') print proc |
使用:
1 2 3 4 5 | import subprocess p = subprocess.Popen("df -h", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate()[0] print p.split(" ") |
它提供了很好的输出,更容易工作:
1 2 3 4 5 6 7 8 9 10 | ['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', ''] |
为了进一步讨论,如果您包括使用Python控制台,您可以从IPython调用外部命令。在IPython提示符中,可以通过前缀'!'调用shell命令。您还可以将Python代码与shell结合起来,并将shell脚本的输出分配给Python变量。
例如:
1 2 3 4 5 6 7 | In [9]: mylist = !ls In [10]: mylist Out[10]: ['file1', 'file2', 'file3',] |
例如(在Linux中):
1 2 | import subprocess subprocess.run('mkdir test.dir', shell=True) |
这将创建测试。当前目录中的目录。注意,这也可以:
1 2 | import subprocess subprocess.call('mkdir test.dir', shell=True) |
使用os的等效代码。系统:
1 2 | import os os.system('mkdir test.dir') |
最佳实践是使用子进程而不是os, .run优先于.call。所有您需要了解的关于子流程的信息都在这里。另外,请注意所有Python文档都可以从这里下载。我下载了打包为.zip的PDF文件。我提到这一点是因为在tutorial.pdf(81页)中有一个很好的os模块概述。此外,它还是Python程序员的权威资源。
在Python中运行外部命令有很多不同的方法,它们都有各自的优点和缺点。
我和我的同事一直在编写Python系统管理工具,所以我们需要运行许多外部命令,有时您希望它们阻塞或异步运行、超时、每秒更新等等。
也有不同的方法来处理返回代码和错误,您可能想要解析输出,并提供新的输入(以expect类型的样式)。或者,您将需要重定向stdin、stdout和stderr,以在不同的tty中运行(例如,在使用screen时)。
因此,您可能需要围绕外部命令编写许多包装器。这是我们写的Python模块,它可以处理几乎任何你想要的,如果不是,它是非常灵活的,所以你可以很容易地扩展它:
https://github.com/hpcugent/vsc-base/blob/master/lib/vsc/utils/run.py
使用Python执行shell命令有两种主要的方式。下面提到的两个示例都展示了如何使用Python获得当前工作目录的名称(
1.第一种方法:可以使用python中的os模块和system()函数来执行python中的shell命令。
1 2 | import os os.system('pwd') |
输出:
1 | /Users/siddharth |
1.第二种方法:另一种方法是使用子进程模块和call()函数。
1 2 | import subprocess subprocess.call('pwd') |
输出:
1 | /Users/siddharth |
用Python调用外部命令
调用外部命令的一种简单方法是使用
1 2 3 | ret = os.system('some_cmd.sh') if ret != 0 : print 'some_cmd.sh execution returned failure' |
在后台用Python调用外部命令
1 2 3 4 5 6 | 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外部命令,并在一段时间后停止
我们甚至可以在后台使用
1 2 3 4 5 | 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] |
对于Python 3.5+,建议使用子进程模块中的run函数。这将返回一个
1 2 3 4 5 | 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) |
使用
1 2 3 4 5 6 | import subprocess .. process = subprocess.Popen(..) # Pass command and arguments to the function stdout, stderr = process.communicate() # Get command output and error .. |
使用subprocess.call:
1 2 3 4 5 6 7 | 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("")) |
一个简单的方法是使用os模块:
1 2 | import os os.system('ls') |
或者,您也可以使用子流程模块
1 2 | import subprocess subprocess.check_call('ls') |
如果你想将结果存储在一个变量中,请尝试:
1 2 | import subprocess r = subprocess.check_output('ls') |
如果需要从Python笔记型计算机(如Jupyter、Zeppelin、Databricks或谷歌Cloud Datalab)调用shell命令,可以使用
例如,
1 | !ls -ilF |
调用命令的方法有很多。
例如:如果
如果我们使用Python脚本来调用
这太难了,所以我们可以用一个空格加入cmd:
1 2 3 | import os cmd ="".join(exename,parameters) os.popen(cmd) |
我写了一个小库来帮助这个用例:
https://pypi.org/project/citizenshell/
它可以安装使用
1 | pip install citizenshell |
然后使用如下:
1 2 | from citizenshell import sh assert sh("echo Hello World") =="Hello World" |
您可以从stderr中分离出stdout,并提取退出代码如下:
1 2 3 4 | result = sh(">&2 echo error && echo output && exit 13") assert result.stdout() == ["output"] assert result.stderr() == ["error"] assert result.exit_code() == 13 |
更酷的是,你不必等到底层shell退出后才开始处理输出:
1 2 | 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 +"!" |
将打印行,因为他们是可用的感谢等待=False
1 2 3 4 | >>> 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找到
经过一些研究,我得到了以下代码,这对我来说非常有用。它基本上同时打印stdout和stderr。希望它能帮助到其他需要它的人。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | 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 |
我推荐以下方法'run',它将帮助我们获得STDOUT, STDERR和退出状态作为字典;调用此函数的人可以通过"run"方法读取返回的字典,以了解进程的实际状态。
1 2 3 4 5 6 7 8 9 10 11 12 | def run (cmd): print"+ DEBUG exec({0})".format(cmd) p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True, shell=True) (out, err) = p.communicate() ret = p.wait() out = filter(None, out.split(' ')) err = filter(None, err.split(' ')) ret = True if ret == 0 else False return dict({'output': out, 'error': err, 'status': ret}) #end |
Eli上面描述的子进程模块非常强大,但是用于进行博格标准系统调用并检查其输出的语法不必要地冗长。
进行系统调用的最简单方法是使用commands模块(仅限Linux)。
1 2 3 | > import commands > commands.getstatusoutput("grep matter alice-in-wonderland.txt") (0,"'Then it doesn't matter which way you go,' said the Cat.") |
元组中的第一项是流程的返回代码。第二项是它的标准输出(和标准错误,合并)。
Python开发人员已经"弃用"了commands模块,但这并不意味着您不应该使用它。只是他们不再开发它了,这没关系,因为它已经很完美了(在它的小而重要的功能上)。
由于其中一些答案与以前的python版本有关,或者使用了
1 2 3 4 5 | 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')) |
正如在文档中提到的,
上述代码的输出为:
1 2 3 4 5 | 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 |
我编写了一个包装器来处理错误和重定向输出等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | import shlex import psutil import subprocess def call_cmd(cmd, stdout=sys.stdout, quiet=False, shell=False, raise_exceptions=True, use_shlex=True, timeout=None): """Exec command by command line like 'ln -ls"/var/log"' """ if not quiet: print("Run %s", str(cmd)) if use_shlex and isinstance(cmd, (str, unicode)): cmd = shlex.split(cmd) if timeout is None: process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell) retcode = process.wait() else: process = subprocess.Popen(cmd, stdout=stdout, stderr=sys.stderr, shell=shell) p = psutil.Process(process.pid) finish, alive = psutil.wait_procs([p], timeout) if len(alive) > 0: ps = p.children() ps.insert(0, p) print('waiting for timeout again due to child process check') finish, alive = psutil.wait_procs(ps, 0) if len(alive) > 0: print('process {} will be killed'.format([p.pid for p in alive])) for p in alive: p.kill() if raise_exceptions: print('External program timeout at {} {}'.format(timeout, cmd)) raise CalledProcessTimeout(1, cmd) retcode = process.wait() if retcode and raise_exceptions: print("External program failed %s", str(cmd)) raise subprocess.CalledProcessError(retcode, cmd) |
你可以这样称呼它:
1 2 3 | cmd = 'ln -ls"/var/log"' stdout = 'out.txt' call_cmd(cmd, stdout) |
如果您没有在命令中使用用户输入,您可以使用这个
1 2 3 4 5 6 | from os import getcwd from subprocess import check_output from shlex import quote def sh(command): return check_output(quote(command), shell=True, cwd=getcwd(), universal_newlines=True).strip() |
把它当做
1 | branch = sh('git rev-parse --abbrev-ref HEAD') |
一些食谱
可用内存(以兆为单位):')[1].split()[1]
')[1].split()[4][0:-1]
')[1:])
但这对用户输入不安全,从文档:
Security Considerations?
Unlike some other popen functions, this implementation will never
implicitly call a system shell. This means that all characters,
including shell metacharacters, can safely be passed to child
processes. If the shell is invoked explicitly, via shell=True, it is
the application’s responsibility to ensure that all whitespace and
metacharacters are quoted appropriately to avoid shell injection
vulnerabilities.When using shell=True, the shlex.quote() function can be used to
properly escape whitespace and shell metacharacters in strings that
are going to be used to construct shell commands.
在使用shell命令的用户输入时,即使使用
1 2 3 4 5 | In [50]: timeit("check_output('ls -l'.split(), universal_newlines=True)", number=1000, globals=globals()) Out[50]: 2.6801227919995654 In [51]: timeit("check_output('ls -l', universal_newlines=True, shell=True)", number=1000, globals=globals()) Out[51]: 3.243950183999914 |