在 Python 中执行 Bash 命令和脚本
对于 Linux 用户来说, shell 命令一定非常熟悉且实用。
如果同时你也在使用 Python,那么你可能已经尝试过通过编程实现自动化,这无疑是节省时间的有效方法。 有时候,你可能还会需要借助 bash 脚本来完成某些自动化任务。
使用 Python 编写脚本通常比 bash 更加方便,而且 Python 脚本的管理也比 bash 脚本更加简单。 尤其是当 bash 脚本变得复杂庞大时,维护会变得相当困难。
那么,如果现在已经存在一个 bash 脚本,而你想在 Python 中运行它,该怎么办呢?
是否有办法在 Python 中执行 bash 命令和脚本呢?
答案是肯定的。 Python 提供了一个内置模块,名为 subprocess
,专门用于在 Python 脚本中执行命令和脚本。 接下来,让我们深入了解如何在 Python 脚本中运行 bash 命令和脚本。
执行 Bash 命令
正如你可能已经了解到的,subprocess
模块是执行 bash 命令和脚本的关键。 它为此提供了多种不同的方法和类。
我们需要重点掌握 subprocess
模块中的一个方法和一个类,它们分别是 run()
和 Popen
。 这两者都能帮助我们在 Python 脚本中执行 bash 命令。 接下来我们逐一进行分析。
subprocess.run()
subprocess.run()
方法接受一个字符串列表作为位置参数。 这是必须的,因为它包含了 bash 命令以及任何参数。 列表中的第一项是命令的名称,其余的项是命令的参数。
让我们来看一个简单的例子。
import subprocess
subprocess.run(["ls"])
以上脚本会列出脚本所在的当前工作目录中的所有项目。 这个命令没有额外的参数,仅仅是基本的 ls
命令。 我们也可以为 ls
命令添加额外的参数,比如 -l
, -a
, -la
等。
下面是一个带有命令参数的例子:
import subprocess
subprocess.run(["ls", "-la"])
以上命令会显示所有文件,包括隐藏文件,以及文件权限等详细信息。 参数 la
使得命令可以显示文件和目录的额外信息以及隐藏文件。
在编写命令时,我们可能会犯一些错误。 这些错误会以错误信息的形式显示。 如果你想捕获这些错误信息并在之后使用它们,应该怎么做呢? 可以使用 stderr
关键字参数来实现。
请看下面的例子。
import subprocess
result = subprocess.run(["cat", "sample.txt"], stderr=subprocess.PIPE, text=True)
print(result.stderr)
请确保你的工作目录中没有名为 sample.txt
的文件。 关键字参数 stderr
的值设为 PIPE
,这会将错误信息返回到一个对象中,我们可以通过该对象的 stderr
属性访问它。 关键字参数 text
则表明输出应该以字符串的形式返回。
类似地,我们可以使用 stdout
关键字参数来捕获命令的输出。
import subprocess
result = subprocess.run(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
print(result.stdout)
subprocess.run()
– 输入
你可以使用 input
关键字参数为命令提供输入。 输入需要以字符串的形式提供。 因此,我们需要将 text
关键字参数设置为 True
。 默认情况下,输入是以字节为单位处理的。
让我们来看一个例子。
import subprocess
subprocess.run(["python3", "add.py"], text=True, input="2 3")
在上述代码中,Python 脚本 add.py
需要接收两个数字作为输入。 我们使用 input
关键字参数为该脚本提供了输入。
subprocess.Popen()
subprocess.Popen()
类比 subprocess.run()
方法更加高级。 它为执行命令提供了更多的选项。 我们会创建一个 subprocess.Popen()
的实例,并使用它来处理各种任务,例如了解命令的执行状态、获取输出、提供输入等。
我们需要了解 subprocess.Popen()
类的一些重要方法。 让我们结合代码示例逐一了解它们。
wait()
wait()
方法用于等待命令执行完成。 在 wait()
方法被调用之后,Python 脚本的下一行代码只有在当前命令完成后才会执行。 请看下面的例子。
import subprocess
process = subprocess.Popen(["ls", "-la"])
print("Completed!")
运行上面的代码并观察输出。 你会发现 “Completed!” 信息会在命令执行之前就打印出来。 为了避免这种情况,我们可以使用 wait()
方法,等待命令执行完成。
import subprocess
process = subprocess.Popen(["ls", "-la"])
process.wait()
print("Completed!")
如果你运行以上代码并观察输出,你会发现 wait()
方法确实有效。 打印语句会在命令执行完毕后才执行。
communicate()
communicate()
方法用于获取命令的输出和错误信息,并可以为命令提供输入。 它返回一个元组,其中包含输出和错误信息。 请看下面的例子。
import subprocess
process = subprocess.Popen(["echo", "Hello, World!"], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
result = process.communicate()
print(result)
subprocess.Popen()
– 输入
我们不能直接将输入传递给 Popen
类。 我们需要使用一个名为 stdin
的关键字参数来为命令提供输入。 Popen
类的实例会为我们提供一个标准输入对象。 这个对象有一个名为 write()
的方法,用于为命令提供输入。
正如之前讨论的,默认情况下输入是以字节流的形式处理的。 因此,不要忘记在创建 Popen
实例时,将 text
关键字参数设置为 True
。
请看下面的例子。
import subprocess
process = subprocess.Popen(["python3", "add.py"], stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
process.stdin.write("2 3")
process.stdin.close()
print(process.stdout.read())
poll()
poll()
方法用于检查命令的执行是否完成。 如果命令仍在执行,这个方法会返回 None
。 让我们来看一个例子。
import subprocess
process = subprocess.Popen(['ping', '-c 5', 'geekflare.com'], stdout=subprocess.PIPE, text=True)
while True:
output = process.stdout.readline()
if output:
print(output.strip())
result = process.poll()
if result is not None:
break
在上述代码中,我们使用 ping
命令发送 5 个请求。 有一个无限循环,它会迭代直到命令执行完成。 我们使用 poll()
方法来检查命令的执行状态。 如果 poll()
方法返回的值不是 None
,则表示执行已经完成。 此时,无限循环会中断。
执行 Bash 脚本
我们已经了解了两种执行命令的方法。 现在,让我们看看如何在 Python 脚本中执行 bash 脚本。
subprocess
模块有一个名为 call()
的方法。 这个方法用于执行 bash 脚本。 该方法会返回 bash 脚本的退出代码。 bash 脚本的默认退出代码是 0。 请看下面的例子。
创建一个名为 practice.sh
的 bash 脚本,内容如下:
#!/bin/bash
echo "Hello, World!"
exit 1
现在,编写一个 Python 脚本来执行上面的 bash 脚本。
import subprocess
exit_code = subprocess.call('./practice.sh')
print(exit_code)
运行以上 Python 脚本后,你将会得到如下输出。
Hello, World!
1
结论
我们已经学习了如何在 Python 中执行 bash 命令和脚本。 你可以使用这些方法来更高效地实现自动化任务。
祝你编码愉快!👨💻