treeコマンドの出力をjson形式に変換する


10

* nixコマンドの「ツリー」の出力をJSON形式に変換する便利な方法はありますか?

編集: 私は自分の問題を十分に説明していなかったと思います。私の目標は次のようなものを変換することです:

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

に:

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

JSONにカプセル化されていることをどのように期待しますか?例と期待される結果を教えてください。
Drav Sloan 2013

@DravSloan例を示すために投稿を編集しました
roundrobin

dir1/dirAサブディレクトリがある場合、何を期待できますか?
cjm

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

@BausTheBig-これまでずっと考えたことはないと思います。treeコマンドは、適切なツールではありません。私は何をするに傾斜されることがありますls -Rfind代わりに。
slm

回答:


6

試行1

perlだけを使用して、ハッシュ構造の単純なハッシュを返すソリューション。OPが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は、unix findコマンドと同様に機能します。JSONモジュールは、Perlの変数を取り、JSONに変換します。

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

現在の作業ディレクトリからファイル構造を反復処理して、process_dir「。」の下の各ファイル/ディレクトリのサブルーチンを呼び出し、見つかったディレクトリごとにno_chdiraを発行しないようにperl に指示chdir()します。

process_dir 現在検査されているファイルがディレクトリでない場合に返されます:

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

次に、既存のハッシュの参照をに%$dirs取り込み$ref、ファイルパスを分割し、各パスに新しいハッシュキー/for追加してループします。

slmのようなディレクトリ構造を作成しました:

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

出力は次のとおりです。

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

試行2

さて、異なるデータ構造で...

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

そして、提案されたディレクトリ構造でスクリプトを実行します...

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

私はこれをかなりうまくやるのが難しいと気づきました(特に、「サブディレクトリの場合はハッシュ、そうでない場合は配列、最上位レベルのOHを指定しない場合は、とにかくハッシュする」ロジックを指定)。これがsed/でできることだったら驚きawkますが、ステファンはまだこれを見ていません:)


ああ、サブディレクトリの形式は少し異なりますが、上記の出力形式が問題になるのでしょうか?
Drav Sloan 2013

ええ、私は自分でそのフォーマットに取り組んできました。それがどのような意味でも標準であるかどうかはわかりませんが、そのようにそれを提供する棚から多くを見つけることができませんが、あなたのアプローチは明確な改善です。
slm

これで何か進歩していますか?8
slm

別の質問で、slm-style-ascii-network-a-gramを使用して側面追跡されました(これは頭が回転していたので、ピットストップです)。カッフィエンと血液の比率を修正するためにクッパを作って、もう一度見てみましょう。
Drav Sloan 2013

asciioはemを作成するためのツールです
slm

13

バージョン1.7はJSONのサポートを含みます:http :
//mama.indstate.edu/users/ice/tree/changes.html

manページごと(の下XML/JSON/HTML OPTIONS):

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

例えば

$ 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

これは、PerlとJSON perlモジュールを使用する1つの方法です。

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

サンプルデータを作成します。

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

これは次のようになります。

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

15 directories, 0 files

これは、Perlコマンドを使用した実行です。

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

これはこの出力を返します:

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

注:これは、からの出力のカプセル化にすぎませんtree。ネストされた階層ではありません。私がこれを提案した後、OPは質問を変更しました!


申し訳ありませんが、私の問題を十分に説明していなかったと思います。私の目標は次のようなものを変換することです:|-dir1 | |-dirA | |-dirB |-dir2 | |-dirA | |-dirB into:{"dir1":["dirA"、 "dirB"]、 "dir2":["dirA"、 "dirB"]}
ラウンドロビン

@BausTheBig-問題ありません。回答を編集して、必要な例を追加します。
slm

OPのデータ構造はPythonオブジェクトのように見えます。私はPythonの知識がほとんどないので手伝うことはできませんが、このような構造の方が簡単にビルドできると思います。
terdon

@terdon-私はそれをDravに任せました、私たちにはハッシュのハッシュ構造のように見えました。
slm

2

Linuxフォルダー/ファイルツリーをJSONまたはXMLファイルに出力する方法も探していました。この単純な端末コマンドを使用しないのはなぜですか。

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

したがって、Linux treeコマンドだけで、独自のパラメーターを構成します。ここで-XはXML出力を示します。私にとってはそれで問題ありません。XMLをJSONに変換するスクリプトがいくつかあると思います。


1

あなたはこのコマンドを試すことができます:

tree -a -J -o *filename*

ファイル名を目的の出力ファイル名に置き換えます。


Jコマンドにはそのようなフラグはありませんtree!!

賛成票:ツリーv1.7.0にはフラグJ ...乾杯があります
drl

0

これは仕事をします。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 = (', ', ' : '))


したがって、これはツリー構造をjsonに変換します。このコードをディレクトリで実行すると、質問に示されているようにjsonが生成されます。
Debodirno Chandra 2018
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.