如何 通过 Python 来调用 Shell 脚本

本文介绍三种写法

  1. 使用os.system 来运行
  2. 使用subprocess.run 来运行
  3. 使用 subprocess.Popen 来运行

三种方式的优缺点

os.systemsubprocess.runsubprocess.Popen
是否需要解析参数noyesyes
同步执行(等待Shell执行结果)yesyesno
能够获得 shell 的输入和输出noyesyes
Shell 执行结果返回值return valueobjectobject

通过 os.system 运行

import os

return_code = os.system('ls -al .')
print("return code:", return_code)

也会将Shell语句的输出输出到的 Python的命令控制台中,但是 Python 的能够获取的返回值,是数字,0 代表 Shell 语句/脚本的执行成功,否则表示Shell执行的状态值。适用于不需要详细的返回信息的 shell 脚本。传入 Shell 命令的参数,都是完整的 String。

➜  tmp python test.py
total 8
drwxr-xr-x    3 jinghui.zhang  staff    96 Nov  8 12:29 .
drwxr-xr-x+ 106 jinghui.zhang  staff  3392 Nov  8 12:29 ..
-rwxr-xr-x@   1 jinghui.zhang  staff    82 Nov  8 12:29 test.py
('return code:', 0)

通过 subprocess 运行

通常推荐使用subprocess 模块来执行 shell 命令。能够相当方便的控制 shell 命令的输入和输出。不同于 os.system 传入的参数是一个数组而不是字符串。

import subprocess

return_code = subprocess.run(['ls', '-al', '.'])
print("return code:", return_code)

执行返回的结果是一个CompletedProcess对象,返回结果如下:

➜  tmp python test.py                                                                                                        
total 8
drwxr-xr-x    3 jinghui.zhang  staff    96 Nov  8 12:34 .
drwxr-xr-x+ 106 jinghui.zhang  staff  3392 Nov  8 12:36 ..
-rwxr-xr-x@   1 jinghui.zhang  staff   103 Nov  8 12:33 test.py
return code: CompletedProcess(args=['ls', '-al', '.'], returncode=0)

如果我们不像它在控制台中,输出 shell 脚本执行的结果,则可以指定subprocess 的 stdout,将其忽略。 如 return_code = subprocess.run(['ls', '-al', '.'], stdout=subprocess.DEVNULL),则输出结果就仅有 Python 程序运行出的结果。

➜  tmp python test.py  
return code: CompletedProcess(args=['ls', '-al', '.'], returncode=0)

如果你想指定 shell 命令的输入参数,可以通过 input 参数来传入。

import subprocess

useless_cat_call = subprocess.run(
    ["cat"], stdout=subprocess.PIPE, text=True, input="Hello from the other side")
print(useless_cat_call.stdout)  # Hello from the other side

上面的 subprocess.run命令中:

  • stdout=subprocess.PIPE 告诉 Python,重定向 Shell 命令的输出,并且这部分输出可以被后面的 Python 程序所调用。
  • text=True 告诉 Python,将 shell 命令的执行的 stdoutstderr 当成字符串. 默认是当成 bytes.
  • input="Hello from the other side" 告诉 Python,shell 命令的输入参数.

上面程序运行的结果是

Hello from the other side

我们可以开启 shell 命令的执行校验,当 shell 的执行出现任何问题,都会抛出一个异常。

import subprocess

failed_command = subprocess.run(["false"], check=True)
print("The exit code was: %d" % failed_command.returncode)

返回结果

$ python3 false_subprocess.py
Traceback (most recent call last):
  File "false_subprocess.py", line 4, in <module>
    failed_command = subprocess.run(["false"], check=True)
  File "/usr/local/python/3.7.5/Frameworks/Python.framework/Versions/3.7/lib/python3.7/subprocess.py", line 512, in run
    output=stdout, stderr=stderr)
subprocess.CalledProcessError: Command '['false']' returned non-zero exit status 1.

通过 subprocess.Popen 运行

subprocess.run 可以看成是 subprocess.Popen 一个简化抽象。subprocess.Popen 能够提供更大的灵活性。

默认情况下,subprocess.Popen 不会中暂停 Python 程序本身的运行(异步)。但是如果你非得同前面两种方式一样,同步运行,你可以加上 .wait() 方法。

import subprocess

list_dir = subprocess.Popen(["ls", "-l"])
list_dir.wait()

当我们仍然处在异步的状况,Python 程序,怎么知道shell 命令是否运行结束与否?可以通过 poll() 来轮询,当放回结果为 None,则表示程序还在运行,否者会返回一个状态码。

list_dir.poll()

如果想,指定输入参数,则需要通过 communicate() 方法。

import subprocess

useless_cat_call = subprocess.Popen(["cat"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output, errors = useless_cat_call.communicate(input="Hello from the other side!")
useless_cat_call.wait()
print(output)
print(errors)

.communicate 方法还会返回标准输入和标准输出,更多细节详见官网 https://docs.python.org/3/library/subprocess.html。

参考文档


  1. Executing Shell Commands with Python
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐