Convertir la salida del comando de árbol a formato json

10

¿Hay alguna forma conveniente de convertir la salida del comando "árbol" * nix al formato JSON?

Editar: creo que no describí mi problema lo suficientemente bien. Mi objetivo es convertir algo como:

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

dentro:

{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin
fuente
¿Cómo esperarías ver eso encapsulado en JSON? ¿Podría dar un ejemplo y los resultados esperados?
Drav Sloan
@DravSloan Edité la publicación para mostrar un ejemplo
roundrobin el
¿Qué esperarías obtener si tuvieras dir1/dirAsubdirectorios?
cjm
{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin
@BausTheBig - No creo que hayas pensado esto hasta el final. El treecomando no es la herramienta correcta. Podría estar inclinado a hacer ls -Ro un finden su lugar.
slm

Respuestas:

6

Intento 1

Una solución que usa solo perl, que devuelve una estructura simple de hash de hashes. Antes de que el OP aclarara el formato de datos 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::FindEl módulo funciona de manera similar al findcomando unix . El JSONmódulo toma variables perl y las convierte en JSON.

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

Recorrerá la estructura de archivos desde el directorio de trabajo actual llamando a la subrutina process_dirpara cada archivo / directorio en ".", Y le no_chdirindicará a perl que no emita un chdir()para cada directorio que encuentre.

process_dir devuelve si el archivo examinado actual no es un directorio:

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

A continuación, cogemos una referencia del hash existentes %$dirsen $ref, dividimos el torno ruta del archivo /y el lazo con forla adición de una nueva clave hash para cada ruta.

Hacer una estructura de directorio como slm hizo:

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

El resultado es:

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

Intento 2

Bien ahora con diferente estructura de datos ...

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

Y luego ejecutando el script en la estructura de directorios propuesta ...

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

Encontré esto bastante difícil de entender (especialmente dado el "hash si subdirectorios, matriz si no, OH A MENOS nivel superior, entonces solo hashes de todos modos" lógica). Así que me sorprendería si esto fuera algo que pudieras hacer con sed/ awk... pero entonces Stephane aún no ha mirado esto, apuesto :)

Drav Sloan
fuente
Oh, el formato para subdirectorios es algo diferente ahora, ¿va a ser un problema el formato de salida anterior?
Drav Sloan
Sí, yo mismo he estado girando en ese formato. No estoy seguro de que sea estándar de ninguna manera, no puedo encontrar mucho de la plataforma que lo proporcione así, pero su enfoque es una mejora definitiva.
slm
¿Haciendo algún progreso con esto? 8-)
slm
Me rastrearon de lado con un slm-style-ascii-network-a-gram en otra pregunta (parada en boxes ya que esta estaba haciendo girar mi cabeza). Haré una taza para corregir mi relación cafeína / sangre y echarle otro vistazo.
Drav Sloan
ASCIIO es la herramienta para hacer em
slm
13

La versión 1.7 incluye soporte para JSON:
http://mama.indstate.edu/users/ice/tree/changes.html

Por la manpágina (debajo XML/JSON/HTML OPTIONS):

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

p.ej

$ 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
fuente
5

Aquí hay una forma de usar Perl y el módulo JSON perl.

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

Ejemplo

Crea algunos datos de muestra.

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

Así es como se ve:

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

15 directories, 0 files

Aquí hay una carrera con el comando Perl:

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

Lo que devuelve esta salida:

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

NOTA: Esto es solo una encapsulación de la salida de tree. No es una jerarquía anidada. ¡El OP cambió la pregunta después de que sugerí esto!

slm
fuente
lo siento, creo que no describí mi problema lo suficientemente bien. Mi objetivo es convertir algo como: | - dir1 | | - dirA | | - dirB | - dir2 | | - dirA | | - dirB en: {"dir1": ["dirA", "dirB"], "dir2": ["dirA", "dirB"]}
roundrobin
@BausTheBig: no hay problema. Edite su respuesta y agregue un ejemplo de lo que desea.
slm
La estructura de datos que parece tener el OP parece un objeto de Python. Casi no tengo conocimiento de Python, así que no puedo evitarlo, pero supongo que este tipo de estructura es más fácil de construir allí.
terdon
@terdon - Se lo dejé a Drav, nos pareció una estructura Hash of Hashes.
slm
2

También estaba buscando una forma de generar una carpeta de Linux / árbol de archivos en algún archivo JSON o XML. ¿Por qué no usar este simple comando de terminal?

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

Entonces, solo el treecomando Linux , y configura tus propios parámetros. Aquí -Xda salida XML. Para mí, está bien, y supongo que hay algún script para convertir XML a JSON.

Roelof Berkepeis
fuente
1

Puedes probar este comando:

tree -a -J -o *filename*

Reemplace el nombre del archivo con el nombre del archivo de salida deseado.


fuente
¡No hay tal bandera Jpara el comando tree!
Voto a favor: en el árbol v1.7.0 hay una bandera J ... aplausos
drl
0

Esto hace el trabajo. 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
fuente
No lo entiendo
Pierre.Vriens
Entonces esto convierte la estructura de árbol a json. Ejecute este código en un directorio y producirá el json como se indica en la pregunta.
Debodirno Chandra