在Python中实现的便携命令执行语法

1 投票
3 回答
707 浏览
提问于 2025-04-15 13:28

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 个回答

0

这是我一个简单的建议:用正则表达式把它解析成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 模块。

1

Ned Batchelder在这篇文章中列出了几十种不同的工具,你可以用它们在Python中解析某种任意语言(虽然我不太明白为什么你不想用Python本身作为“定义一组要执行的命令的语言”,但我相信你有自己的理由)。Ned提到的工具之一是pyparsing,正如Gregg所说,但还有三十多种其他工具,所以在选择之前,你可能想先看看,找一个适合你的。

一旦你把输入的源语言转换成语法树或其他方便的内存表示形式,你就可以遍历这棵树并逐步执行(插入变量值、根据条件和循环进行分支等)。除了能够运行外部进程(例如通过subprocess,无论是像Gregg建议的那样封装,还是不封装),别忘了Python本身也能轻松执行一些基本命令,而且在可行的情况下,这样做会比把执行交给子进程快得多(实际上,Perl早期成功的一个原因就是它能在一个进程内完成很多事情,而sh则疯狂地分叉;现代的sh后代,比如bash和ksh,吸取了这个教训,现在实现了很多可以在主脚本同一进程内执行的内置命令;-)。

例如,你示例中的delete命令可以通过os.unlink“内部”实现(现在无法链接到Python在线文档,因为python.org由于硬件问题暂时无法访问;-)。

2

听起来你需要结合使用PyParsingPython的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

撰写回答