将树命令的输出转换为json格式


10

有没有简便的方法可以将* nix命令“ tree”的输出转换为JSON格式?

编辑: 我认为我对问题的描述不够充分。我的目标是转换类似:

.
|-- dir1
|   |-- dirA
|   |   |-- dirAA
|   |   `-- dirBB
|   `-- dirB
`-- dir2
    |-- dirA
    `-- dirB

变成:

{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}

您希望如何看到它封装在JSON中?您能举个例子和预期的结果吗?
Drav Sloan

@DravSloan我编辑了帖子以显示示例
roundrobin 2013年

如果dir1/dirA有子目录,您期望得到什么?
cjm

{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin 2013年

@BausTheBig-我不认为您一直都这么想。该tree命令不是正确的工具。我可能倾向于这样做,ls -R或者find相反。
slm

Answers:


6

尝试1

仅使用perl的解决方案,返回哈希结构的简单哈希。在OP之前澄清了JSON的数据格式。

#! /usr/bin/perl

use File::Find;
use JSON;

use strict;
use warnings;

my $dirs={};
my $encoder = JSON->new->ascii->pretty;

find({wanted => \&process_dir, no_chdir => 1 }, ".");
print $encoder->encode($dirs);

sub process_dir {
    return if !-d $File::Find::name;
    my $ref=\%$dirs;
    for(split(/\//, $File::Find::name)) {
        $ref->{$_} = {} if(!exists $ref->{$_});
        $ref = $ref->{$_};
    }
}

File::Find模块的工作方式与unix find命令类似。该JSON模块采用perl变量并将其转换为JSON。

find({wanted => \&process_dir, no_chdir => 1 }, ".");

将迭代当前工作目录中的文件结构,process_dir为“。”下的每个文件/ 目录调用子例程,并no_chdir告诉perl不要为chdir()找到的每个目录发出a 。

process_dir 如果当前检查的文件不是目录,则返回:

return if !-d $File::Find::name;

然后,我们将现有哈希的引用捕获%$dirs$ref,将文件路径拆分,/for为每个路径添加新的哈希键进行循环。

制作像slm这样的目录结构可以做到:

mkdir -p dir{1..5}/dir{A,B}/subdir{1..3}

输出为:

{
   "." : {
      "dir3" : {
         "dirA" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         },
         "dirB" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         }
      },
      "dir2" : {
         "dirA" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         },
         "dirB" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         }
      },
      "dir5" : {
         "dirA" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         },
         "dirB" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         }
      },
      "dir1" : {
         "dirA" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         },
         "dirB" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         }
      },
      "dir4" : {
         "dirA" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         },
         "dirB" : {
            "subdir2" : {},
            "subdir3" : {},
            "subdir1" : {}
         }
      }
   }
}

尝试2

现在可以使用不同的数据结构了...

#! /usr/bin/perl

use warnings;
use strict;
use JSON;

my $encoder = JSON->new->ascii->pretty;   # ascii character set, pretty format
my $dirs;                                 # used to build the data structure

my $path=$ARGV[0] || '.';                 # use the command line arg or working dir

# Open the directory, read in the file list, grep out directories and skip '.' and '..'
# and assign to @dirs
opendir(my $dh, $path) or die "can't opendir $path: $!";
my @dirs = grep { ! /^[.]{1,2}/ && -d "$path/$_" } readdir($dh);
closedir($dh);

# recurse the top level sub directories with the parse_dir subroutine, returning
# a hash reference.
%$dirs = map { $_ => parse_dir("$path/$_") } @dirs;

# print out the JSON encoding of this data structure
print $encoder->encode($dirs);

sub parse_dir {
    my $path = shift;    # the dir we're working on

    # get all sub directories (similar to above opendir/readdir calls)
    opendir(my $dh, $path) or die "can't opendir $path: $!";
    my @dirs = grep { ! /^[.]{1,2}/ && -d "$path/$_" } readdir($dh);
    closedir($dh);

    return undef if !scalar @dirs; # nothing to do here, directory empty

    my $vals = [];                            # set our result to an empty array
    foreach my $dir (@dirs) {                 # loop the sub directories         
        my $res = parse_dir("$path/$dir");    # recurse down each path and get results

        # does the returned value have a result, and is that result an array of at 
        # least one element, then add these results to our $vals anonymous array 
        # wrapped in a anonymous hash
        # ELSE
        # push just the name of that directory our $vals anonymous array
        push(@$vals, (defined $res and scalar @$res) ? { $dir => $res } : $dir);
    }

    return $vals;  # return the recursed result
}

然后在建议的目录结构上运行脚本...

./tree2json2.pl .
{
   "dir2" : [
      "dirB",
      "dirA"
   ],
   "dir1" : [
      "dirB",
      {
         "dirA" : [
            "dirBB",
            "dirAA"
         ]
      }
   ]
}

我发现这个非常棘手的技巧非常正确(尤其是考虑到“如果子目录为哈希,如果不是则为数组,除非顶层,否则无论如何都只是散列”逻辑)。因此,如果这是您可以使用sed/ awk... 可以做的事,我会感到惊讶,但随后Stephane尚未对此进行研究:)


哦,sub dirs的格式现在有些不同了,上述输出格式会成为问题吗?
Drav Sloan 2013年

是的,我本人一直在尝试这种格式。我不确定它是否以任何方式都是标准的,无法像现成的那样找到很多现成的东西,但是您的方法是绝对的改进。
slm

取得任何进展吗?8
slm

我在另一个问题上跟踪了一个slm风格的ascii网络网络语法(进站,因为这使我的脑袋旋转)。我会做杯茶来纠正我的咖啡因/血比,然后再看一遍。
Drav Sloan

asciio是制作em的工具
slm

13

1.7版包含对JSON的支持:http :
//mama.indstate.edu/users/ice/tree/changes.html

每页man(在下方XML/JSON/HTML OPTIONS):

-J     Turn on JSON output. Outputs the directory tree as an JSON formatted array.

例如

$ tree -J                                                                                                 

/home/me/trash/tree-1.7.0
[{"type":"directory","name": ".","contents":[
    {"type":"file","name":"CHANGES"},
    {"type":"file","name":"color.c"},
    {"type":"file","name":"color.o"},
    {"type":"directory","name":"doc","contents":[
      {"type":"file","name":"tree.1"},
      {"type":"file","name":"tree.1.fr"},
      {"type":"file","name":"xml.dtd"}
    ]},
    {"type":"file","name":"hash.c"},
    {"type":"file","name":"hash.o"},
    {"type":"file","name":"html.c"},
    {"type":"file","name":"html.o"},
    {"type":"file","name":"INSTALL"},
    {"type":"file","name":"json.c"},
    {"type":"file","name":"json.o"},
    {"type":"file","name":"LICENSE"},
    {"type":"file","name":"Makefile"},
    {"type":"file","name":"README"},
    {"type":"file","name":"strverscmp.c"},
    {"type":"file","name":"TODO"},
    {"type":"file","name":"tree"},
    {"type":"file","name":"tree.c"},
    {"type":"file","name":"tree.h"},
    {"type":"file","name":"tree.o"},
    {"type":"file","name":"unix.c"},
    {"type":"file","name":"unix.o"},
    {"type":"file","name":"xml.c"},
    {"type":"file","name":"xml.o"}
  ]},
  {"type":"report","directories":1,"files":26}
]

5

这是使用Perl和JSON perl模块的一种方法。

$ tree | perl -e 'use JSON; @in=grep(s/\n$//, <>); \
     print encode_json(\@in)."\n";'

创建一些样本数据。

$ mkdir -p dir{1..5}/dir{A,B}

看起来是这样的:

$ tree 
.
|-- dir1
|   |-- dirA
|   `-- dirB
|-- dir2
|   |-- dirA
|   `-- dirB
|-- dir3
|   |-- dirA
|   `-- dirB
|-- dir4
|   |-- dirA
|   `-- dirB
`-- dir5
    |-- dirA
    `-- dirB

15 directories, 0 files

这是使用Perl命令的运行:

$ tree | perl -e 'use JSON; @in=grep(s/\n$//, <>); print encode_json(\@in)."\n";'

返回以下输出:

[".","|-- dir1","|   |-- dirA","|   `-- dirB","|-- dir2","|   |-- dirA","|   `-- dirB","|-- dir3","|   |-- dirA","|   `-- dirB","|-- dir4","|   |-- dirA","|   `-- dirB","`-- dir5","    |-- dirA","    `-- dirB","","15 directories, 0 files"]

注意:这只是对输出的封装tree。不是嵌套的层次结构。我建议了这个之后,OP改变了问题!


抱歉,我认为我对问题的描述不够充分。我的目标是转换类似:|-dir1 | |-dirA | |-dirB |-dir2 | |-dirA | |-dirB进入:{“ dir1”:[“ dirA”,“ dirB”],“ dir2”:[“ dirA”,“ dirB”]}
roundrobin

@BausTheBig-没问题。编辑您的答案,并添加所需示例。
slm

OP似乎遵循的数据结构看起来像一个Python对象。我几乎不了解Python,所以我无能为力,但我想这种结构在那儿更容易构建。
terdon

@terdon-我把它留给了Drav,对我们来说,它就像是一个哈希表哈希结构。
slm

2

我也在寻找一种将linux文件夹/文件树输出到某些JSON或XML文件的方法。为什么不使用这个简单的终端命令:

tree --dirsfirst --noreport -n -X -i -s -D -f -o my.xml

因此,仅需使用Linux tree命令并配置您自己的参数即可。这里-X给出了XML输出。对我来说,这没关系,我想有一些脚本可以将XML转换为JSON。


1

您可以尝试以下命令:

tree -a -J -o *filename*

将文件名替换为所需的输出文件名。


J命令没有这样的标记tree

Upvote:在树v1.7.0上有一个标志J ...干杯
drl

1

这样就可以了。https://gist.github.com/debodirno/18a21df0511775c19de8d7ccbc99cb72

import os
import sys
import json

def tree_path_json(path):
    dir_structure = {}
    base_name = os.path.basename(os.path.realpath(path))
    if os.path.isdir(path):
        dir_structure[base_name] = [ tree_path_json(os.path.join(path, file_name))\
         for file_name in os.listdir(path) ]
    else:
        return os.path.basename(path)
    return dir_structure

if len(sys.argv) > 1:
    path = sys.argv[1]
else:
    path = '.'

print json.dumps(tree_path_json(path), indent = 4, separators = (', ', ' : '))

我不明白
Pierre.Vriens

1
因此,这会将树结构转换为json。在目录上运行此代码,它将生成问题中指示的json。
Debodirno Chandra
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.