AWK递归树结构

时间:2018-11-26 16:14:56

标签: bash awk graphviz dot

我正在尝试分析包含层次结构中的行的文件。例如文件:

a b c
a b d
a B C
A B C

表示a包含bBb包含cdB包含{ {1}}。 C包含一个不同的A,其中包含自己的B

这很像文件列表。

我想用分层的方括号将其格式化,例如:

C

我无法提出一种体面的方法来做到这一点。我以为AWK是我最好的选择,但是没有具体说明如何实现它。

上下文

我的输入实际上是文件列表。当然,如果需要,我当然可以用空格分隔字段,或者将其保留为a { b { c d } B { C } } A { B { C } } 。这些文件是无序的,并在编译时通过检查从代码库生成。我想要的输出将是一个graphviz DOT文件,每个文件都包含在其自己的子图中。

因此输入:

/

输出应为

a/b/c
a/b/d
a/B/C
A/B/C

graphviz output

有人知道我如何完成此处理吗?我也可以使用其他工具,而不仅仅是AWK。

注意:尽管我可以根据需要预先计算最大深度,但是深度不是固定的。并非所有叶子的深度都相同。

2 个答案:

答案 0 :(得分:2)

如果深度固定为3个水平

gawk -F/ '
    {f[$1][$2][$3] = 1}
    END {
        n = 0
        print "digraph {"
        for (a in f) {
            print "  subgraph cluster_" a " {"
            print "    label = " a
            for (b in f[a]) {
                print "    subgraph cluster_" b " {"
                print "      label = " b
                for (c in f[a][b]) {
                    printf "      node_%d [label=%s]\n", ++n, c
                }
                print "    }"
            }
            print "  }"
        }
        print "}"
    }
' file
digraph {
  subgraph cluster_A {
    label = A
    subgraph cluster_B {
      label = B
      node_1 [label=C]
    }
  }
  subgraph cluster_a {
    label = a
    subgraph cluster_B {
      label = B
      node_2 [label=C]
    }
    subgraph cluster_b {
      label = b
      node_3 [label=c]
      node_4 [label=d]
    }
  }
}

如果深度是任意的,事情就会变得复杂。

答案 1 :(得分:2)

  

我也可以使用其他工具,而不仅仅是AWK。

我提供了以下Python解决方案:

import sys

INDENT = '  '
NODE_COUNT = 1


def build(node, l):
    x = l[0]
    if x not in node:
        node[x] = {}

    if len(l) > 1:
        build(node[x], l[1:])


def indent(s, depth):
    print('%s%s' % (INDENT * depth, s))


def print_node(label, value, depth):

    if len(value.keys()) > 0:
        indent('subgraph cluster_%s {' % label, depth)
        indent('  label = %s' % label, depth)
        for child in value:
            print_node(child, value[child], depth+1)
        indent('}', depth)
    else:
        global NODE_COUNT
        indent('node_%d [label=%s]' % (NODE_COUNT, label), depth)
        NODE_COUNT += 1


def main():

    d = {}

    for line in sys.stdin:
        build(d, [x.strip() for x in line.split()])

    print('digraph {')
    for k in d.keys():
        print_node(k, d[k], 1)
    print('}')


if __name__ == '__main__':
    main()

结果:

$ cat rels.txt
a b c
a b d
a B C
A B C

$ cat rels.txt | python3 make_rels.py
digraph {
  subgraph cluster_a {
    label = a
    subgraph cluster_b {
      label = b
      node_1 [label=c]
      node_2 [label=d]
    }
    subgraph cluster_B {
      label = B
      node_3 [label=C]
    }
  }
  subgraph cluster_A {
    label = A
    subgraph cluster_B {
      label = B
      node_4 [label=C]
    }
  }
}