跳到内容

CommandLib

Commandlib 是一个无依赖库,用于以干净、可读的方式在构建脚本中调用外部 UNIX 命令(例如)。

使用方法链接,你可以构建 Command 对象,这些对象在特定目录中运行,并具有指定的环境变量PATH等。

为了简单起见,该库本身只以阻塞方式运行命令(所有命令在继续之前都运行到完成),尽管它包含通过icommandlibpexpect运行非阻塞命令的钩子。

假装“django/manage.py”

# Pretend django "manage.py" that just prints out arguments:
import sys ; sys.stdout.write(' '.join(sys.argv[1:]))

from commandlib import Command

# Create base command
python = Command("python")

# Create command "python manage.py" that runs in the django directory
manage = python("manage.py").in_dir("django")

# Build even more specific command
dev_manage = manage.with_trailing_args("--settings", "local_settings.py")
# Run combined command
dev_manage("runserver", "8080").run()

将输出

runserver 8080 --settings local_settings.py

安装

$ pip install commandlib

文档

为什么?

Commandlib 避免了直接使用 subprocess 库(Popen、call、check_output()、.communicate() 等)以及由此产生的混淆而导致的混乱代码。

这是一个高度狗粮的库。

subprocess 真的有那么糟糕吗?

代码可能会更长、更乱。例如,来自堆栈溢出

import subprocess, os
previous_directory = os.getcwd()

os.chdir("command_directory")
my_env = os.environ.copy()
my_env["PATH"] = "/usr/sbin:/sbin:" + my_env["PATH"]
subprocess.Popen(my_command, env=my_env)
os.chdir(previous_directory)

等同于

from commandlib import Command

Command(my_command).with_path("/usr/sbin:/sbin:").in_dir("command_directory").run()

为什么不使用 Delegator(Kenneth Reitz 的“面向人类的子进程”)?

Kenneth Reitz(requests 的作者,“面向人类的 urllib2/3”)编写了一个类似的“面向人类的子进程”,称为envoy。现在已弃用,现在有一个称为delegator的替代方案,它是一个非常薄的 subprocess 包装器。

delegator 具有而 commandlib 没有的功能

  • Delegator 可以像 bash 一样链接命令(delegator.chain('fortune | cowsay'))。Commandlib 不这样做,因为在对该库进行狗粮测试时,我从未遇到过需要这样做的情况。但是,你可以很容易地使用 .output() 获取一个命令的输出作为字符串,并使用 piped.from_string(string) 将其馈送到另一个命令中。

  • Delegator 以阻塞和非阻塞方式运行子进程(使用 pexpect)。commandlib 本身只执行阻塞,但如果你 pip install pexpect 或 icommandlib,它可以通过其中任何一个运行。

  • 在 Windows 上运行

两者都具有的功能

  • 能够设置环境变量。
  • 能够从命令对象运行 pexpect 进程。

commandlib 独有的功能

  • 能够轻松地设置 PATH。
  • 能够轻松地从当前虚拟环境调用代码。
  • 能够管道输入字符串或文件,以及轻松地将管道输出到字符串或文件(或文件句柄)。
  • 用于从当前虚拟环境轻松运行命令的钩子。

为什么不使用其他工具?

  • os.system(*) - 只能运行非常简单的 bash 命令。

  • sh - 使用了很多魔法。试图让 Python 更像 shell,而不是让运行命令更 Pythonic。

  • plumbum - 类似于 amoffat 的 sh,试图在 Python 中创建一种“bash”。它还使用了一种奇怪的方式从字典语法构建命令(grep["-v", "\.py"])。