พยายาม 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::Find
โมดูลทำงานในลักษณะคล้ายกับfind
คำสั่งunix JSON
โมดูลใช้เวลาตัวแปร Perl และแปลงให้เป็น JSON
find({wanted => \&process_dir, no_chdir => 1 }, ".");
จะทำซ้ำโครงสร้างของไฟล์จากไดเร็กทอรีการทำงานปัจจุบันที่เรียกรูทีนย่อยprocess_dir
สำหรับแต่ละไฟล์ / ไดเร็กทอรีภายใต้ "." และคำสั่งno_chdir
perl จะไม่ออก a 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 UNLESS ระดับบนสุด, แล้วแค่แฮชต่อไป" ตรรกะ) ดังนั้นฉันจะแปลกใจถ้านี่เป็นสิ่งที่คุณสามารถทำได้ด้วยsed
/ awk
... แต่แล้วสเตฟานยังไม่ได้ดูสิ่งนี้ แต่ฉันเดิมพัน :)