Konvertieren Sie die Ausgabe des Befehls tree in das JSON-Format


10

Gibt es eine bequeme Möglichkeit, die Ausgabe des * nix-Befehls "tree" in das JSON-Format zu konvertieren?

Edit: Ich glaube, ich habe mein Problem nicht gut genug beschrieben. Mein Ziel ist es, etwas zu konvertieren wie:

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

in:

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

Wie würden Sie erwarten, dass dies in JSON gekapselt ist? Können Sie ein Beispiel und erwartete Ergebnisse nennen?
Drav Sloan

@ DravSloan Ich habe den Beitrag bearbeitet, um ein Beispiel zu zeigen
Roundrobin

Was würden Sie erwarten, wenn Sie dir1/dirAUnterverzeichnisse hätten?
cjm

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

@BausTheBig - Ich glaube nicht, dass du das den ganzen Weg durchdacht hast. Der treeBefehl ist nicht das richtige Werkzeug. Ich könnte dazu geneigt sein ls -Roder findstattdessen.
slm

Antworten:


6

Versuch 1

Eine Lösung, die nur Perl verwendet und eine einfache Hash-of-Hash-Struktur zurückgibt. Vor dem OP wurde das Datenformat von JSON geklärt.

#! /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::FindDas Modul funktioniert ähnlich wie der Unix- findBefehl. Das JSONModul nimmt Perl-Variablen und konvertiert sie in JSON.

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

Durchläuft die Dateistruktur aus dem aktuellen Arbeitsverzeichnis, wobei die Unterroutine process_dirfür jede Datei / jedes Verzeichnis unter "." no_chdirAufgerufen wird , und weist das Perl an, nicht chdir()für jedes gefundene Verzeichnis ein a auszugeben.

process_dir Gibt zurück, wenn die aktuell untersuchte Datei kein Verzeichnis ist:

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

Wir greifen dann auf eine Referenz des vorhandenen Hashs %$dirszu $ref, teilen den Dateipfad auf /und führen eine Schleife durch, forindem wir für jeden Pfad einen neuen Hash-Schlüssel hinzufügen.

Erstellen einer Verzeichnisstruktur wie bei slm:

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

Die Ausgabe ist:

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

Versuch 2

Okay jetzt mit unterschiedlicher Datenstruktur ...

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

Und dann das Skript auf der vorgeschlagenen Verzeichnisstruktur ausführen ...

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

Ich fand es verdammt schwierig, das richtig zu machen (besonders angesichts der Logik "Hash wenn Unterverzeichnisse, Array wenn nicht, OH UNLESS Top Level, dann nur Hashes"). Ich wäre also überrascht, wenn Sie damit etwas anfangen könnten sed/ awk... aber Stephane hat sich das noch nicht angesehen, wette ich :)


Oh, das Format für Unterverzeichnisse ist jetzt etwas anders. Wird das obige Ausgabeformat ein Problem sein?
Drav Sloan

Ja, ich habe mich selbst mit diesem Format beschäftigt. Ich bin mir nicht sicher, ob es in irgendeiner Weise Standard ist, kann nicht viel von der Stange finden, das es so liefert, aber Ihr Ansatz ist eine deutliche Verbesserung.
slm

Fortschritte machen? 8-)
slm

Bei einer anderen Frage wurde ich mit einem slm-style-ascii-network-a-Gramm von der Seite verfolgt (Boxenstopp, da dieser meinen Kopf drehte). Ich mache eine Tasse Kaffee, um mein Koffein / Blut-Verhältnis zu korrigieren und einen weiteren Blick darauf zu werfen.
Drav Sloan

asciio ist das Werkzeug, um em
slm

13

Version 1.7 unterstützt JSON:
http://mama.indstate.edu/users/ice/tree/changes.html

Auf der manSeite (unter XML/JSON/HTML OPTIONS):

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

z.B

$ 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

Hier ist eine Möglichkeit, Perl und das JSON-Perl-Modul zu verwenden.

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

Beispiel

Erstellen Sie einige Beispieldaten.

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

So sieht es aus:

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

15 directories, 0 files

Hier ist ein Lauf mit dem Perl-Befehl:

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

Welches gibt diese Ausgabe zurück:

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

HINWEIS: Dies ist nur eine Kapselung der Ausgabe von tree. Keine verschachtelte Hierarchie. Das OP hat die Frage geändert, nachdem ich dies vorgeschlagen habe!


Entschuldigung, ich glaube, ich habe mein Problem nicht gut genug beschrieben. Mein Ziel ist es, etwas zu konvertieren wie: | - dir1 | | - dirA | | - dirB | - dir2 | | - dirA | | - dirB in: {"dir1": ["dirA", "dirB"], "dir2": ["dirA", "dirB"]}
Roundrobin

@BausTheBig - kein Problem. Bearbeiten Sie Ihre Antwort und fügen Sie ein Beispiel für das hinzu, was Sie möchten.
slm

Die Datenstruktur, nach der das OP zu suchen scheint, sieht aus wie ein Python-Objekt. Ich habe fast keine Kenntnisse über Python, daher kann ich nicht anders, aber ich würde vermuten, dass diese Art von Struktur dort einfacher aufzubauen ist.
Terdon

@terdon - Ich habe es Drav überlassen, es sah für uns wie eine Hash of Hashes-Struktur aus.
slm

2

Ich suchte auch nach einer Möglichkeit, einen Linux-Ordner / Dateibaum in eine JSON- oder XML-Datei auszugeben. Warum nicht diesen einfachen Terminalbefehl verwenden:

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

Also nur den Linux- treeBefehl und konfigurieren Sie Ihre eigenen Parameter. Hier -Xgibt XML-Ausgabe. Für mich ist das in Ordnung, und ich denke, es gibt ein Skript zum Konvertieren von XML in JSON.


1

Sie könnten diesen Befehl versuchen:

tree -a -J -o *filename*

Ersetzen Sie den Dateinamen durch den gewünschten Namen der Ausgabedatei.


Es gibt keine solche Flagge Jfür den Befehl tree!!

Upvote: auf Baum v1.7.0 gibt es eine Flagge J ...
Prost

0

Das macht den Job. 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 = (', ', ' : '))

Ich verstehe es nicht
Pierre.Vriens

Dies konvertiert also die Baumstruktur in json. Führen Sie diesen Code in einem Verzeichnis aus, und es wird der in der Frage angegebene JSON erzeugt.
Debodirno Chandra
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.