如何 通过 Python 来调用 Shell 脚本
本文介绍三种写法
- 使用
os.system
来运行 - 使用
subprocess.run
来运行 - 使用
subprocess.Popen
来运行
三种方式的优缺点
os.system | subprocess.run | subprocess.Popen | |
---|---|---|---|
是否需要解析参数 | no | yes | yes |
同步执行(等待Shell执行结果) | yes | yes | no |
能够获得 shell 的输入和输出 | no | yes | yes |
Shell 执行结果返回值 | return value | object | object |
通过 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 命令的执行的stdout
和stderr
当成字符串. 默认是当成 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。
参考文档