【问题标题】:Make my awk shell script more efficient (parsing python)让我的 awk shell 脚本更高效(解析 python)
【发布时间】:2015-01-21 15:37:57
【问题描述】:

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

我想出了以下内容:

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

依次是

  • 查找所有 python 文件
  • 其中,grep 查找以 importfrom 开头的行
  • 拆分: 字符并使用后面的内容,因此不包括文件名和输出编号
  • 如果该行的格式为from foo import bar,则打印foo,否则如果它的格式为import foo,则打印foo
  • 用换行符替换逗号和空格,对于像import a, b, c 这样的行
  • 对输出进行排序并获取唯一性

我自己破解了这个,但我想它可能会更好。你会怎么做?合并awks?

【问题讨论】:

  • 显示您正在解析的数据的示例,但从外观上看,除了 find 之外的所有内容都可以在单个 awk 脚本中完成
  • 请注意,对于分布在多行的导入,这将失败;但也许这在代码中不存在。我假设你已经安全地运行了上面的代码。
  • @JID 如果您愿意,我可以发布一个示例,但它都是各种类型的 python 脚本,我想在其中检测字符串 pygal.styletime 中的 pygal.style 等内容在import time 内。 @Evert - 你是对的。我确实对样本进行了人工审核,我相信我不需要担心这种情况。

标签: python bash shell awk


【解决方案1】:

一开始的设置相当巧妙,但有几个地方可以清理:

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

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

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

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

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

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

【讨论】:

    【解决方案2】:

    为特定结构抓取源代码非常脆弱,在很多情况下它可能会失败。例如:

    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
    

    【讨论】:

    • 我假设~/svn 他正在寻找正在使用和未使用的代码。我同意这是最“正确”的方法。
    【解决方案3】:

    这是我的合并 awk:

    /^[ \t]*import .* as/  {
        sub("^[ \t]+","");          # remove leading whitespace
        sub("[ \t]*#.*","");        # remove comments
        print $2;
        next;
    }
    /^[ \t]*from (.*) import/ {
        sub("^[ \t]+","");          # remove leading whitespace
        sub("[ \t]*#.*","");        # remove comments
        print $2;
        next;
    }
    /^[ \t]*import (.*)/  {
        sub("^[ \t]+","");          # remove leading whitespace
        sub("[ \t]*#.*","");        # remove comments
        split(substr($0,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代码。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-11-30
      • 1970-01-01
      • 2011-02-08
      • 1970-01-01
      • 1970-01-01
      • 2021-11-11
      • 1970-01-01
      相关资源
      最近更新 更多