让我的 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
依次是
- 找到所有 python 个文件
- 其中,greps 以
import
或 from
开头的行
- 拆分
:
字符并使用后面的内容,因此不包括文件名和输出编号
- 如果该行的格式为
from foo import bar
,则打印 foo
,否则如果其格式为 import foo
,则打印 foo
- 用换行符替换逗号和空格,例如
import a, b, c
- 对输出进行排序并取唯一值
我自己把它拼凑在一起,但我想它可能会更好。你会怎么做?合并 awk
s?
开始的设置非常巧妙,但有几个地方可以清理:
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= 之前 sort
ing 和 uniq
-ing 来加快执行速度] 你的 awk
和 sed
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 代码。
我最近的任务是审核我的团队在整个生产代码存储库中使用的所有 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
依次是
- 找到所有 python 个文件
- 其中,greps 以
import
或from
开头的行
- 拆分
:
字符并使用后面的内容,因此不包括文件名和输出编号 - 如果该行的格式为
from foo import bar
,则打印foo
,否则如果其格式为import foo
,则打印foo
- 用换行符替换逗号和空格,例如
import a, b, c
- 对输出进行排序并取唯一值
我自己把它拼凑在一起,但我想它可能会更好。你会怎么做?合并 awk
s?
开始的设置非常巧妙,但有几个地方可以清理:
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= 之前 sort
ing 和 uniq
-ing 来加快执行速度] 你的 awk
和 sed
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 代码。