根据一行中的条件从多个文件中提取行

Extract lines from multiple files based on condition in one line

我有许多目录包含具有如下模式的文本文件:

class FeatureFE():
    meta_data = MetaData(
        name='COOL_FEATURE',
        sub_type='EXTRA_COOL_FEATURES',
        required_data=[accounts, logs],
        has_graph=True,
        x_axis_label='Time',
        y_axis_label='Foo',
        graph_caption='Description of my feature',
        priority='low',
    )

我的任务是检查每个 .py 文件,如果 has_graph=True,则提取 namerequired_datagraph_caption - 最终目标是 CSV 结构如下:

name, required_data, graph_caption
'COOL_FEATURE', [accounts, logs],'Description of my feature',

awk/sed/grep 这似乎绝对可行,但我正在努力实现目标。到目前为止,我已经做到了:

grep -E -B 4 -A 5 "has_graph=True" feature_17.py | tr -s ' ' | grep '^ name\|^ required_data\|^ graph_caption' | sed 's/.*=//'

哪个returns

'COOL_FEATURE',
[accounts, logs],
'Description of my feature',

对于一个文件,但当 运行 on *.py.

时什么也没有

非常感谢帮助!

每当您的数据中有名称=值对时,我发现最好先创建这些映射的数组,然后简单地通过名称访问这些值。例如,使用 GNU awk 将第三个参数匹配 () 和 ENDFILE:

$ cat tst.awk
BEGIN {
    OFS = ","
    numNames = split("name required_data graph_caption",names)
}

match([=10=],/^\s*(\w+)\s*=\s*(.*\S)\s*,\s*$/,a) {
    name  = a[1]
    value = a[2]
    name2value[name] = value
}

ENDFILE {
    if ( name2value["has_graph"] == "True" ) {
        if ( !doneHdr++ ) {
            for (nameNr=1; nameNr<=numNames; nameNr++) {
                name = names[nameNr]
                printf "%s%s", name, (nameNr<numNames ? OFS : ORS)
            }
        }
        for (nameNr=1; nameNr<=numNames; nameNr++) {
            name  = names[nameNr]
            value = name2value[name]
            gsub(/"/,"\"\"",value)
            printf "\"%s\"%s", value, (nameNr<numNames ? OFS : ORS)
        }
    }
    delete name2value
}

$ awk -f tst.awk file
name,required_data,graph_caption
"'COOL_FEATURE'","[accounts, logs]","'Description of my feature'"

我在打印前添加了双引号,以确保输出是有效的 CSV,即使您的值包含 ,(如 [accounts, logs] 那样)and/or 双引号。

要将上述内容与 find 一起使用,我会这样做:

find . -name '*.py' -exec awk -f tst.awk {} +

但先删除这部分脚本:

        if ( !doneHdr++ ) {
            for (nameNr=1; nameNr<=numNames; nameNr++) {
                name = names[nameNr]
                printf "%s%s", name, (nameNr<numNames ? OFS : ORS)
            }
        }

所以你不会为每批从 find 传递给 awk 的文件打印一次 header 行,只需稍后手动添加 header 行或在 [=29= 之前打印它] 剧本。还有其他方法可以解决这个问题,但这是最简单的。

能否请您尝试以下操作(考虑到您的 python 文件只会出现 1 次此 class)。在 GNU awk.

中测试和编写
awk '
BEGIN{
  FS="="
  s1="7"
  OFS=","
  print "name, required_data, graph_caption"
}
/has_graph=True/{
  found=1
}
found && /name/{
  sub(/,/,"",)
  name=
  next
}
found && /required_data/{
  sub(/,/,"",)
  data=
}
found && /graph_caption/{
  sub(/,/,"",)
  print s1 name s1,s1 data s1,s1  s1
  nextfile
}
'  *.py

Perl 解决方案:

perl -0777 -nE 'for my $key (qw( name required_data graph_caption )) {
                  ($h{$key}) = /\b$key=(.*),/;
                }
                say join ",", @h{qw{ name required_data graph_caption }};
               ' -- *.py
  • -n逐条读取输入记录,对每一条执行代码
  • -0777读取整个文件而不是逐行读取
  • %h 哈希值填充了从正则表达式匹配中捕获的值,\b 代表 "word boundary"