* nixコマンドの「ツリー」の出力をJSON形式に変換する便利な方法はありますか?
編集:私は自分の問題を十分に説明していなかったと思います。私の目標は次のようなものを変換することです:
.
|-- dir1
| |-- dirA
| | |-- dirAA
| | `-- dirBB
| `-- dirB
`-- dir2
|-- dirA
`-- dirB
に:
{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
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::Find
_モジュールは、unix find
コマンドと同様に機能します。 JSON
モジュールはPerl変数を受け取り、それらをJSONに変換します。
_find({wanted => \&process_dir, no_chdir => 1 }, ".");
_
"。"の下の各ファイル/ディレクトリに対してサブルーチン_process_dir
_を呼び出す現在の作業ディレクトリからファイル構造を反復処理し、_no_chdir
_は、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" : {}
}
}
}
}
_
さて、異なるデータ構造で...
_#! /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 UNLESSトップレベル、次にとにかくハッシュする」ロジックを指定した場合)。ですから、これがsed
/awk
でできることだったら驚きますが、Stephaneはまだこれを見ていません:)
バージョン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}
]
これは、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は質問を変更しました!
Linuxフォルダー/ファイルツリーをJSONまたはXMLファイルに出力する方法も探していました。この単純な端末コマンドを使用しないのはなぜですか。
tree --dirsfirst --noreport -n -X -i -s -D -f -o my.xml
したがって、Linux tree
コマンドだけで、独自のパラメーターを構成します。ここに -X
はXML出力を提供します。私にとってはそれで問題ありません。XMLをJSONに変換するスクリプトがいくつかあると思います。
あなたはこのコマンドを試すことができます:
tree -a -J -o *filename*
ファイル名を目的の出力ファイル名に置き換えます。
これは仕事をします。 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 = (', ', ' : '))