Apakah ada cara mudah untuk mengonversi output dari perintah *nix "tree" ke format JSON?
Sunting:
Saya rasa saya tidak cukup menggambarkan masalah saya. Tujuan saya adalah mengonversi sesuatu seperti:
.
|-- dir1
| |-- dirA
| | |-- dirAA
| | `-- dirBB
| `-- dirB
`-- dir2
|-- dirA
`-- dirB
menjadi:
{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}
Jawaban yang Diterima:
Percobaan 1
Solusi hanya menggunakan Perl, mengembalikan hash sederhana dari struktur hash. Sebelum
OP mengklarifikasi format data 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
modul bekerja dengan cara yang mirip dengan unix find
memerintah. JSON
module mengambil variabel Perl dan mengubahnya menjadi JSON.
find({wanted => &process_dir, no_chdir => 1 }, ".");
Akan mengulangi struktur file dari direktori kerja saat ini dengan memanggil subrutin process_dir
untuk setiap file/direktori di bawah “.”, dan no_chdir
beri tahu Perl untuk tidak mengeluarkan chdir()
untuk setiap direktori yang ditemukannya.
process_dir
kembali jika file yang diperiksa saat ini bukan direktori:
return if !-d $File::Find::name;
Kami kemudian mengambil referensi dari hash yang ada %$dirs
ke $ref
, bagi jalur file di sekitar /
dan loop dengan for
menambahkan kunci hash baru untuk setiap jalur.
Membuat struktur direktori seperti yang dilakukan slm:
mkdir -p dir{1..5}/dir{A,B}/subdir{1..3}
Outputnya adalah:
{
"." : {
"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" : {}
}
}
}
}
Percobaan 2
Oke sekarang dengan struktur data yang berbeda…
#! /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
}
Dan kemudian menjalankan skrip pada struktur direktori yang diusulkan…
./tree2json2.pl .
{
"dir2" : [
"dirB",
"dirA"
],
"dir1" : [
"dirB",
{
"dirA" : [
"dirBB",
"dirAA"
]
}
]
}
Saya menemukan ini sangat sulit untuk diperbaiki (terutama mengingat "hash jika sub direktori, array jika tidak, OH KECUALI tingkat atas, maka hanya hash") logika). Jadi
Saya akan terkejut jika ini adalah sesuatu yang dapat Anda lakukan dengan sed
/ awk
… tapi kemudian
Stephane belum melihat ini, saya yakin