Converti l'output del comando tree in formato json


10

C'è un modo conveniente per convertire l'output del comando "albero" * nix in formato JSON?

Modifica: penso di non aver descritto il mio problema abbastanza bene. Il mio obiettivo è convertire qualcosa del tipo:

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

in:

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

Come ti aspetteresti di vederlo incapsulato in JSON? Potresti dare un esempio e risultati attesi?
Drav Sloan,

@DravSloan Ho modificato il post per mostrare un esempio
roundrobin,

Cosa ti aspetteresti di ottenere se dir1/dirAavessi delle sottodirectory?
cjm,

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

@BausTheBig - Non penso che tu l'abbia pensato fino in fondo. Il treecomando non è lo strumento giusto. Potrei essere propenso a fare ls -Ro findinvece.
slm

Risposte:


6

Tentativo 1

Una soluzione che usa solo perl, restituendo un semplice hash di struttura di hash. Prima dell'OP chiariva il formato dei dati di 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::FindIl modulo funziona in modo simile al findcomando unix . Il JSONmodulo prende le variabili perl e le converte in JSON.

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

Scorrerà la struttura del file dalla directory di lavoro corrente chiamando la subroutine process_dirper ciascun file / directory in "." E no_chdirdirà a perl di non emettere un chdir()per ogni directory che trova.

process_dir ritorna se il file esaminato attuale non è una directory:

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

Quindi prendiamo un riferimento dell'hash esistente %$dirsin $ref, dividiamo il percorso del file /e eseguiamo il ciclo con l' foraggiunta di una nuova chiave hash per ogni percorso.

Creare una struttura di directory come ha fatto slm:

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

L'output è:

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

Tentativo 2

Va bene ora con diversa struttura dei dati ...

#! /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 quindi eseguendo lo script sulla struttura di directory proposta ...

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

Ho trovato questo dannatamente complicato da ottenere (soprattutto dato la logica "hash if sub directory, array if not, OH UNLESS top level, then hashas comunque"). Quindi sarei sorpreso se questo fosse qualcosa che potevi fare con sed/ awk... ma poi Stephane non l'ha ancora visto, scommetto :)


Oh, il formato per le directory secondarie è un po 'diverso ora, il formato di output sopra sarà un problema?
Drav Sloan,

Sì, ho girato su quel formato me stesso. Non sono sicuro che sia standard in alcun modo, non riesco a trovare molto dallo scaffale che lo fornirà in questo modo, ma il tuo approccio è un netto miglioramento.
slm

Stai facendo progressi con questo? Cool
slm

Sono stato monitorato di lato con una rete ascii-stile-ascii-network-a-gram su un'altra domanda (pit-stop in quanto questo mi faceva girare la testa). Farò una tazza per correggere il mio rapporto caffiene / sangue e dare un'altra occhiata.
Drav Sloan,

asciio è lo strumento per creare em
slm

13

La versione 1.7 include il supporto per JSON:
http://mama.indstate.edu/users/ice/tree/changes.html

Per la manpagina (sotto XML/JSON/HTML OPTIONS):

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

per esempio

$ 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

Ecco un modo per usare Perl e il modulo perl JSON.

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

Esempio

Crea alcuni dati di esempio.

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

Ecco come appare:

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

15 directories, 0 files

Ecco una corsa usando il comando Perl:

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

Che restituisce questo output:

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

NOTA: questo è solo un incapsulamento dell'output di tree. Non una gerarchia nidificata. L'OP ha cambiato la domanda dopo che l'ho suggerito!


scusate penso di non aver descritto il mio problema abbastanza bene. Il mio obiettivo è convertire qualcosa del tipo: | - dir1 | | - dirA | | - dirB | - dir2 | | - dirA | | - dirB in: {"dir1": ["dirA", "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin,

@BausTheBig - nessun problema. Modifica la tua risposta e aggiungi un esempio di ciò che desideri.
slm

La struttura dei dati che l'OP sembra seguire sembra un oggetto Python. Non ho quasi alcuna conoscenza di Python, quindi non posso fare a meno, ma immagino che questo tipo di struttura sia più facile da costruire lì.
Terdon

@terdon - L'ho lasciato a Drav, sembrava una struttura Hash of Hash per noi.
slm

2

Stavo anche cercando un modo per generare un albero di file / cartelle linux su alcuni file JSON o XML. Perché non usare questo semplice comando da terminale:

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

Quindi, basta il treecomando Linux e configura i tuoi parametri. Qui -Xfornisce un output XML. Per me va bene, e immagino ci sia qualche script per convertire XML in JSON.


1

Puoi provare questo comando:

tree -a -J -o *filename*

Sostituisci il nome file con il nome del file di output desiderato.


Non esiste una bandiera del genere Jper il comando tree!!

Upvote: sull'albero v1.7.0 c'è una bandiera J ... salute
drl

0

Questo fa il lavoro. 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 = (', ', ' : '))

Non capisco
Pierre.Vriens,

Quindi questo converte la struttura ad albero in json. Esegui questo codice su una directory e produrrà il json come indicato nella domanda.
Debodirno Chandra,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.