在Python中实现的便携命令执行语法
Python在定义一系列要执行的命令方面并不是特别好,而Bash则做得不错。不过,Bash在Windows上不能直接运行。
背景:我正在尝试构建一组程序,这些程序之间有明确的依赖关系,目标是在mac、Windows和Linux上都能运行。类似于macports,但要能在这三种平台上都能使用。
这就需要一种类似于shell的语言,用来定义执行的命令顺序,以便构建、修补等特定程序,比如:
post-destroot {
xinstall -d ${destroot}${prefix}/share/emacs/${version}/leim
delete ${destroot}${prefix}/bin/ctags
delete ${destroot}${prefix}/share/man/man1/ctags.1
if {[variant_isset carbon]} {
global version
delete ${destroot}${prefix}/bin/emacs ${destroot}${prefix}/bin/emacs-${version}
}
}
上面的内容来自于macports中的emacs Portfile。注意这里使用了变量、条件语句和函数,除了简单列出要按顺序执行的命令。
虽然语法不是Python的,但实际执行的过程需要通过Python来实现,通常是用subprocess或其他方法。简单来说,我应该能够“运行”这样的脚本,但每个命令都会调用一个指定的钩子函数,这个函数会实际执行传入的命令。我希望你能理解这个意思。
3 个回答
这是我一个简单的建议:用正则表达式把它解析成Python代码,这样就变成了
def post_destroot():
xinstall ('-d', '${destroot}${prefix}/share/emacs/${version}/leim')
delete ('${destroot}${prefix}/bin/ctags')
delete ('${destroot}${prefix}/share/man/man1/ctags.1')
if test('variant_isset', 'carbon'):
global('version')
delete('${destroot}${prefix}/bin/emacs', '${destroot}${prefix}/bin/emacs-${version}')
我觉得写 xinstall()
、delete()
和 test()
这些函数并不难,特别是因为Python已经有内置的功能可以格式化字符串,比如 "{destroot}".format(dictionary).
但是为什么要这么麻烦呢?可以试试看看标准库里的 distutils
模块。
Ned Batchelder在这篇文章中列出了几十种不同的工具,你可以用它们在Python中解析某种任意语言(虽然我不太明白为什么你不想用Python本身作为“定义一组要执行的命令的语言”,但我相信你有自己的理由)。Ned提到的工具之一是pyparsing,正如Gregg所说,但还有三十多种其他工具,所以在选择之前,你可能想先看看,找一个适合你的。
一旦你把输入的源语言转换成语法树或其他方便的内存表示形式,你就可以遍历这棵树并逐步执行(插入变量值、根据条件和循环进行分支等)。除了能够运行外部进程(例如通过subprocess,无论是像Gregg建议的那样封装,还是不封装),别忘了Python本身也能轻松执行一些基本命令,而且在可行的情况下,这样做会比把执行交给子进程快得多(实际上,Perl早期成功的一个原因就是它能在一个进程内完成很多事情,而sh则疯狂地分叉;现代的sh后代,比如bash和ksh,吸取了这个教训,现在实现了很多可以在主脚本同一进程内执行的内置命令;-)。
例如,你示例中的delete
命令可以通过os.unlink“内部”实现(现在无法链接到Python在线文档,因为python.org由于硬件问题暂时无法访问;-)。
听起来你需要结合使用PyParsing和Python的Subprocess模块。
虽然有关于Subprocess的MOTW文章,但我觉得它有点复杂,所以我经常使用这种包装代码。
from subprocess import Popen, PIPE
def shell(args, input=None):
p = Popen(args, stdin=PIPE, stdout=PIPE, stderr=PIPE)
stdout, stderr = p.communicate(input=input)
return stdout, stderr