Bash AWK递归树结构

Bash AWK递归树结构,bash,awk,graphviz,dot,Bash,Awk,Graphviz,Dot,我试图解析一个文件,它包含层次结构中的行。例如,文件: a b c a b d a B C A B C 表示a包含b和b,b包含c和d,b包含cA包含一个不同的B,它包含自己的C 这很像一个文件列表 我想以一种分层次的方式对其进行格式化,如: a { b { c d } B { C } } A { B { C } } 我想不出一个像样的方法来做这件事。我原以为AWK是我最好的选择,

我试图解析一个文件,它包含层次结构中的行。例如,文件:

a b c
a b d
a B C
A B C
表示
a
包含
b
b
b
包含
c
d
b
包含
c
<代码>A包含一个不同的
B
,它包含自己的
C

这很像一个文件列表

我想以一种分层次的方式对其进行格式化,如:

a {
    b {
        c
        d
    }
    B {
        C
    }
}
A {
    B {
        C
    }
}
我想不出一个像样的方法来做这件事。我原以为AWK是我最好的选择,但没想到如何真正实现它

上下文 我的输入实际上是一个文件列表。如果需要,我当然可以用空格分隔字段,或者用
/
保存它们。这些文件是无序的,在编译时通过检查从代码库生成。我想要的输出是一个graphviz点文件,每个文件都包含在它自己的子图中

因此,对于输入:

a/b/c
a/b/d
a/B/C
A/B/C
输出将是

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]
      }
  }
}

有人知道我怎么处理吗?我对其他工具也持开放态度,不仅仅是AWK


注意:深度不是固定的,但如果需要,我可以预先计算最大深度。也不是所有的叶子都在同一个深度。

如果深度固定在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

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

如果深度固定在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
如果深度是任意的,事情就会变得复杂

我对其他工具也持开放态度,不仅仅是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]
    }
  }
}
我对其他工具也持开放态度,不仅仅是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]
    }
  }
}

如果您有
a/b/c
a/b/d
,它会是什么样子?i、 e.子集群中有2个叶节点?我刚刚编辑了示例以显示。是否可以有3个以上的级别,如
a b d x
a b d p q r
?在结尾添加了一个注释,并作了进一步的说明,但可以有3个以上的级别,并且在任何级别都可以有叶。您提到的输入是无序的。这是否意味着
a b d p q r
记录可以位于
w x y z
之后,而
a b c
可以位于文件的顶部?如果您有
a/b/c
a/b/d
,它会是什么样子?i、 e.子集群中有2个叶节点?我刚刚编辑了示例以显示。是否可以有3个以上的级别,如
a b d x
a b d p q r
?在结尾添加了一个注释,并作了进一步的说明,但可以有3个以上的级别,并且在任何级别都可以有叶。您提到的输入是无序的。这是否意味着
abdpqr
记录也可以位于
wxyz
之后,而
abc
可以位于文件的顶部?不幸的是,深度是任意的,所以事情很复杂。但对于固定深度,这是一个很好的解决方案!不幸的是,深度是任意的,所以事情很复杂。但对于固定深度,这是一个很好的解决方案!出于我的目的,这项工作需要修改。它不像我希望的那样干净,但是完成了工作:)这对我来说是经过修改的。它不像我希望的那么干净,但却完成了任务:)