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

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 :)


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

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!


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.


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é.


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 = (', ', ' : '))

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
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.