让我的 awk shell 脚本更有效率(解析 python)

Make my awk shell script more efficient (parsing python)

我最近的任务是审核我的团队在整个生产代码存储库中使用的所有 python 模块。

我想到了以下内容:

find ~/svn/ -name *.py 
| xargs grep -hn "^import\|^from"
| awk -F ":" '{print }' 
| awk '{if (/from/) print ; else { = ""; print [=12=]} }' 
| sed 's/,\| /\n/g' 
| sort 
| uniq > /tmp/pythonpkgs.txt 

依次是

我自己把它拼凑在一起,但我想它可能会更好。你会怎么做?合并 awks?

开始的设置非常巧妙,但有几个地方可以清理:

1: find ~/svn/ -name *.py 
2: | xargs grep -hn "^import\|^from"
3: | awk -F ":" '{print }' 
4: | awk '{if (/from/) print ; else { = ""; print [=10=]} }' 
5: | sed 's/,\| /\n/g' 
6: | sort 
7: | uniq > /tmp/pythonpkgs.txt 

第 3 行:您不需要第一个 awk split/print——只是不要在 grep 中包含 -n,这样您就不会在输出中包含行号。

time find ./<<my_large_project>> -name *.py 
| xargs grep -hn "^import\|^from" 
| awk '{if (/from/) print ; else { = ""; print [=11=]} }' 
| sed 's/,\| /\n/g' 
| sort 
| uniq
~~snip~~
real    0m0.492s
user    0m0.208s
sys     0m0.116s

第 6-7 行和第 4-5 行:如果有很多重复行,可以通过在 [=28= 之前 sorting 和 uniq-ing 来加快执行速度] 你的 awksed

time find ./<<my_large_project>> -name *.py 
| xargs grep -hn "^import\|^from" 
| sort 
| uniq 
| awk '{if (/from/) print ; else { = ""; print [=12=]} }' 
| sed 's/,\| /\n/g'
~~snip~~
real    0m0.464s
user    0m0.224s
sys     0m0.140s

请注意,这将错过 PEP 0328 中所述的多行导入。支持这些导入将使您的正则表达式搜索相对重要,因为您必须寻找可选的括号并记下之前的空格。

针对特定构造的 grepping 源代码非常脆弱,在很多情况下它可能会失败。考虑一下,例如:

import foo ; print 123

import foo, \
   bar

 str = '''
 import foo
 '''

等等

如果您对更健壮的方法感兴趣,可以通过以下方式使用 python 自己的编译器可靠地解析导入的名称:

import ast

def list_imports(source):
    for node in ast.walk(ast.parse(source)):
        if isinstance(node, ast.Import):
            for name in node.names:
                yield name.name
        if isinstance(node, ast.ImportFrom):
            yield node.module

用法:

 for name in sorted(set(list_imports(some_source))):
     print name

这是我的综合 awk:

/^[ \t]*import .* as/  {
    sub("^[ \t]+","");          # remove leading whitespace
    sub("[ \t]*#.*","");        # remove comments
    print ;
    next;
}
/^[ \t]*from (.*) import/ {
    sub("^[ \t]+","");          # remove leading whitespace
    sub("[ \t]*#.*","");        # remove comments
    print ;
    next;
}
/^[ \t]*import (.*)/  {
    sub("^[ \t]+","");          # remove leading whitespace
    sub("[ \t]*#.*","");        # remove comments
    split(substr([=10=],7),a,",");  # split on commas
    for (i=1;i<=length(a);i++) {
        gsub("[ \t]+","",a[i]); # trim whitespace
        print a[i];
    }
    next;
}

致电:

find . -name '*.py' -exec awk -f scan.awk {} \; | sort | uniq

如前所述,它不会处理一些潜在的情况,例如用“;”连接的行或用 '\' 拆分,或用 '()' 分组,但它会覆盖大部分 Python 代码。