Converter saída do comando tree para o formato json

8

Existe uma maneira conveniente de converter a saída da "árvore" do comando * nix para o formato JSON?

Editar: Acho que não descrevi bem meu problema. Meu objetivo é converter algo como:

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

para:

{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
    
por roundrobin 10.09.2013 / 22:16

6 respostas

5

Tentativa 1

Uma solução usando apenas perl, retornando um hash simples de estrutura de hashes. Antes de o OP esclareceu o formato de dados do 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->{$_};
    }
}
O módulo

File::Find funciona de maneira semelhante ao comando unix find . O módulo JSON usa variáveis perl e as converte em JSON.

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

Irá percorrer a estrutura de arquivos a partir do diretório de trabalho atual chamando a sub-rotina process_dir para cada arquivo / diretório em "." e no_chdir informará perl para não emitir chdir() para cada diretório encontrado.

process_dir retornará se o arquivo examinado atual não for um diretório:

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

Em seguida, pegamos uma referência do hash %$dirs existente em $ref , dividimos o caminho do arquivo em / e fazemos um loop com for adicionando uma nova chave hash para cada caminho.

Criando uma estrutura de diretórios como o slm:

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

A saída é:

{
   "." : {
      "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" : {}
         }
      }
   }
}

Tentativa 2

Agora, com estrutura de dados diferente ...

#! /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
}

E, em seguida, executando o script na estrutura de diretórios proposta ...

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

Eu achei isso muito difícil de acertar (especialmente tendo em conta o "hash se subdiretórios, array, se não, OH menos de nível superior, então apenas hashes de qualquer maneira" lógica). assim Eu ficaria surpreso se isso fosse algo que você pudesse fazer com sed / awk ... mas depois Stephane não olhou para isso, mas aposto:)

    
por 11.09.2013 / 00:16
10

A versão 1.7 inclui suporte para JSON: link

Na página man (em XML/JSON/HTML OPTIONS ):

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

por exemplo.

$ 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}
]
    
por 20.12.2014 / 02:24
5

Aqui está uma maneira de usar Perl e o módulo JSON perl.

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

Exemplo

Crie alguns dados de amostra.

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

Veja como fica:

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

15 directories, 0 files

Aqui está uma corrida usando o comando Perl:

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

Que retorna esta saída:

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

NOTA: Este é apenas um encapsulamento da saída de tree . Não é uma hierarquia aninhada. O OP mudou a questão depois que eu sugeri isso!

    
por 10.09.2013 / 22:30
2

Eu também estava procurando uma maneira de gerar uma árvore de pastas / arquivos do Linux para algum arquivo JSON ou XML. Por que não usar este comando simples do terminal:

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

Assim, apenas o comando tree do Linux e configura seus próprios parâmetros. Aqui -X fornece saída XML. Para mim, tudo bem, e acho que há algum script para converter XML em JSON.

    
por 04.08.2014 / 03:16
0

Isso faz o trabalho. link

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 = (', ', ' : '))
    
por 22.10.2018 / 06:08
-1

Você pode tentar este comando:

tree -a -J -o *filename*

Substitua o nome do arquivo pelo nome do arquivo de saída desejado.

    
por 12.09.2018 / 19:37