用于从文件中选择单个 Python 函数的 Bash 脚本
Posted
技术标签:
【中文标题】用于从文件中选择单个 Python 函数的 Bash 脚本【英文标题】:Bash script to select a single Python function from a file 【发布时间】:2012-05-17 17:56:38 【问题描述】:对于git alias problem,我希望能够按名称从文件中选择单个 Python 函数。例如:
...
def notyet():
wait for it
def ok_start(x):
stuff
stuff
def dontgettrickednow():
keep going
#stuff
more stuff
def ok_stop_now():
在算法方面,以下内容已经足够接近:
-
找到匹配
/^(\s*)def $1[^a-zA-Z0-9]/
的行时开始过滤
(我真的不在乎以下函数之前的装饰器是否被拾取。结果是供人类阅读的。)
我试图用 Awk(我几乎不知道)来做到这一点,但这比我想象的要难一些。对于初学者,我需要一种在原始def
之前存储缩进长度的方法。
【问题讨论】:
【参考方案1】:使用awk
的一种方式。代码注释很好,所以我希望它很容易理解。
infile
的内容:
...
def notyet():
wait for it
def ok_start(x):
stuff
stuff
def dontgettrickednow():
keep going
#stuff
more stuff
def ok_stop_now():
script.awk
的内容:
BEGIN
## 'f' variable is the function to search, set a regexp with it.
f_regex = "^" f "[^a-zA-Z0-9]"
## When set, print line. Otherwise omit line.
## It is set when found the function searched.
## It is unset when found any character different from '#' with less
## spaces before it.
in_func = 0
## Found function.
$1 == "def" && $2 ~ f_regex
## Get position of first 'd' in the line.
i = index( $0, "d" )
## Sanity check. Never should success because the condition was
## checked before.
if ( i == 0 )
next
## Get characters until matched index before, check that all of
## them are spaces, and get its length.
indent = substr( $0, 0, i - 1 )
if ( indent ~ /^[[:space:]]*$/ )
num_spaces = length( indent )
## Set variable, print line and read next one.
in_func = 1
print
next
## When we are inside the function, line doesn't begin with '#' and
## it's not a blank line (only spaces).
in_func == 1 && $1 ~ /^[^#]/ && $0 ~ /[^[:space:]]/
## Get how many characters there are until first non-space. The result
## is the position of first non-blank, so substract one to get the number
## of spaces.
spaces = match( $0, /[^[:space:]]/ )
spaces -= 1
## If current indent is less or equal that the indent of function definition, then
## end of function found, so end processing.
if ( spaces <= num_spaces )
in_func = 0
## Self-explanatory.
in_func == 1
print
像这样运行它:
awk -f script.awk -v f="ok_start" infile
输出如下:
def ok_start(x):
stuff
stuff
def dontgettrickednow():
keep going
#stuff
more stuff
【讨论】:
哇,太棒了 :) 我可以确认它适用于我的真实世界功能。一个有趣的方面是,如果有两个同名的函数(如果它们在不同的类中可能会发生),它会端到端地返回它们。不确定这是否是有意的(或者确实应该是正确的行为) - 似乎是一个不错的结果。 @SteveBennett:我只是在编辑以修改该行为。现在它应该只处理找到的第一个函数。 我实际上有点喜欢“选择所有同名函数”版本——否则我不确定你如何访问第二个同名函数。 @SteveBennett:好的。已修复旧行为。【参考方案2】:为什么不让 python 来做呢?我认为inspection
模块可以打印出函数的源代码,所以你可以导入模块,选择函数并检查它。不挂断。正在为您寻找解决方案...
好的。事实证明,inspect.getsource
函数不适用于交互定义的内容:
>>> def test(f):
... print 'arg:', f
...
>>> test(1)
arg: 1
>>> inspect.getsource(test)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "C:\Python27\lib\inspect.py", line 699, in getsource
lines, lnum = getsourcelines(object)
File "C:\Python27\lib\inspect.py", line 688, in getsourcelines
lines, lnum = findsource(object)
File "C:\Python27\lib\inspect.py", line 529, in findsource
raise IOError('source code not available')
IOError: source code not available
>>>
但对于您的用例,它会起作用:对于保存到磁盘的模块。以我的test.py
文件为例:
def test(f):
print 'arg:', f
def other(f):
print 'other:', f
并与此交互式会话进行比较:
>>> import inspect
>>> import test
>>> inspect.getsource(test.test)
"def test(f):\n print 'arg:', f\n"
>>> inspect.getsource(test.other)
"def other(f):\n print 'other:', f\n"
>>>
所以...您需要编写一个简单的 python 脚本,该脚本接受 python 源文件的名称和函数/对象名称作为参数。然后它应该导入模块并检查函数并将其打印到 STDOUT。
【讨论】:
我喜欢你的想法,但看起来 Python 必须能够解析整个项目——这意味着检查所有内容,而不仅仅是单个文件。 (否则无法导入,因为依赖坏了) 天哪。好吧,在这种情况下,还有 python 编译器包可以提供帮助:docs.python.org/library/compiler.html - 但这需要更多的工作!以上是关于用于从文件中选择单个 Python 函数的 Bash 脚本的主要内容,如果未能解决你的问题,请参考以下文章
从AWS Lambda python函数将多个JSON文件合并到S3中的单个JSON文件
从 boost::python::object 列表中获取单个元素,用于 python 例程