Convertir la sortie de la commande d'arborescence au format json

10

Existe-t-il un moyen pratique de convertir la sortie de la "arborescence" de la commande * nix au format JSON?

Edit: Je pense que je n'ai pas assez bien décrit mon problème. Mon objectif est de convertir quelque chose comme:

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

dans:

{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin
la source
Comment vous attendriez-vous à voir cela encapsulé dans JSON? Pourriez-vous donner un exemple et les résultats attendus?
Drav Sloan
@DravSloan J'ai édité le post pour montrer un exemple
roundrobin
À quoi vous attendriez-vous si vous disposiez de dir1/dirAsous-répertoires?
cjm
{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin
@BausTheBig - Je ne pense pas que vous ayez bien réfléchi à tout cela. La treecommande n'est pas le bon outil. Je pourrais être enclin à faire ls -Rou à la findplace.
slm

Réponses:

6

Tentative 1

Une solution utilisant juste perl, renvoyant un simple hachage de structure de hachage. Avant le format de données clarifié OP de 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::Findmodule fonctionne de manière similaire à la findcommande unix . Le JSONmodule prend des variables perl et les convertit en JSON.

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

Itère la structure de fichiers du répertoire de travail actuel en appelant le sous-programme process_dirde chaque fichier / répertoire sous ".", Et no_chdirindique à perl de ne pas émettre de fichier chdir()pour chaque répertoire qu'il trouve.

process_dir renvoie si le fichier examiné actuel n'est pas un répertoire:

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

Nous saisissons alors une référence du hachage existant %$dirsdans $refREFENDU le chemin du fichier autour /et boucle avec l' forajout d' une nouvelle clé de hachage pour chaque chemin.

Faire une structure de répertoire comme slm a fait:

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

La sortie est:

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

Tentative 2

Bon maintenant avec une structure de données différente ...

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

Et puis exécuter le script sur la structure de répertoires proposée ...

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

J'ai trouvé ce sacrément difficile à obtenir correctement (surtout compte tenu de la logique "hachage si sous-répertoires, tableau sinon, OH UNLESS de niveau supérieur, puis hachage de toute façon"). Je serais donc surpris si c'était quelque chose que vous pourriez faire avec sed/ awk... mais alors Stéphane n'a pas encore regardé ça, je parie :)

Drav Sloan
la source
Oh, le format des sous-répertoires est quelque peu différent maintenant, le format de sortie ci-dessus va-t-il être un problème?
Drav Sloan
Oui, je tourne moi-même sur ce format. Je ne suis pas sûr que ce soit standard de quelque manière que ce soit, je ne trouve pas grand-chose qui le fournira comme ça, mais votre approche est une nette amélioration.
slm
Faire des progrès avec ça? 8-)
slm
Je me suis fait suivre avec un réseau ascii-style-slm-style-slm sur une autre question (arrêt au stand car celui-ci me faisait tourner la tête). Je vais faire une tasse de thé pour corriger mon rapport café / sang et y jeter un autre coup d'œil.
Drav Sloan
asciio est l'outil pour faire em
slm
13

La version 1.7 inclut la prise en charge de JSON:
http://mama.indstate.edu/users/ice/tree/changes.html

Par la manpage (sous XML/JSON/HTML OPTIONS):

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

par exemple

$ 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}
]
Sridhar Sarnobat
la source
5

Voici une façon d'utiliser Perl et le module Perl JSON.

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

Exemple

Créez des exemples de données.

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

Voici à quoi ça ressemble:

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

15 directories, 0 files

Voici une course utilisant la commande Perl:

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

Qui renvoie cette sortie:

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

REMARQUE: il s'agit simplement d'une encapsulation de la sortie de tree. Pas une hiérarchie imbriquée. L'OP a changé la question après avoir suggéré cela!

slm
la source
désolé, je pense que je n'ai pas assez bien décrit mon problème. Mon objectif est de convertir quelque chose comme: | - dir1 | | - dirA | | - dirB | - dir2 | | - dirA | | - dirB dans: {"dir1": ["dirA", "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin
@BausTheBig - pas de problème. Modifiez votre réponse et ajoutez un exemple de ce que vous voulez.
slm
La structure de données que l'OP semble rechercher ressemble à un objet Python. Je n'ai presque aucune connaissance de Python, donc je ne peux pas m'empêcher mais je suppose que ce type de structure est plus facile à construire là-bas.
terdon
@terdon - Je l'ai laissé à Drav, cela ressemblait à une structure Hash of Hashes pour nous.
slm
2

Je cherchais également un moyen de sortir une arborescence de dossiers / fichiers Linux vers un fichier JSON ou XML. Pourquoi ne pas utiliser cette simple commande de terminal:

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

Donc, juste la treecommande Linux et configurez vos propres paramètres. Voici -Xune sortie XML. Pour moi, c'est OK, et je suppose qu'il y a un script pour convertir XML en JSON.

Roelof Berkepeis
la source
1

Vous pouvez essayer cette commande:

tree -a -J -o *filename*

Remplacez le nom de fichier par le nom de votre fichier de sortie souhaité.


la source
Il n'y a pas un tel indicateur Jpour la commande tree!!
Vote positif: sur l'arbre v1.7.0, il y a un drapeau J ... cheers
drl
0

Cela fait le travail. 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 = (', ', ' : '))
Debodirno Chandra
la source
Je ne comprends pas
Pierre.Vriens
Donc, cela convertit la structure arborescente en json. Exécutez ce code sur un répertoire et il produira le json comme indiqué dans la question.
Debodirno Chandra