Reputation: 513
I have the following perl script from this stack exchange question that converts the content of a directory into JSON.
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->{$_};
}
}
I'm running the script using Termux on Android 6.0.
Consider the following directory:
.
|--server .
| | - File 1.tmp
| | - File 2.sql
| | - File 3.sql
|-- js .
| | - File 1.js
| | - File 2.js
| | - File 3.js
| -css .
| | - File 1.js
| | - File 2.js
| | - File 3.js
| -assets .
| | - Font-awesome
| | .
| | | - webfont.
| | | | - File 2.woff
| | | | - File 2.woff
| | | | - File 3.woff
| | | - css .
| | | | - File 2.css
| | | | - File 2.css
| | | | - File 3.css
| | - Fonts
| | | - ps .
| | | | - File 2.woff
| | | | - File 2.css
| | | | - File 3.txt
| | - images
| | | - File 1.png
| | | - File 2.png
| | | - File 3.svg
The script returns the following if I execute it in the above directory:
{
"." : {
"server" : {},
"js" : {},
"css" : {},
"assets" : {
"fonts" : {
"ps":{}
},
"images" : {},
"font-awesome" : {
"css" :{},
"webfonts" : {}
}
}
}
}
I'm trying to edit the script to additionally include the files in each subdirectory as a Js array (if it's possible using perl + JSON Module, or, just Perl)
Eg [Edit]
{
"." : {
"server" : {"Files": ["File1", "File2", "File n"]},
/* Or just "server" : ["File1", "File2", "File n"], ... */
"js" : {"Files": ["File1", "File2", "File n"]},
"assets" : {
"fonts" : {
"ps":{"Files": ["File1", "File2", "File n"]}
},
"images" : {"Files": ["File1", "File2", "File n"]},
"font-awesome" : {
"css" : {"Files": ["File1", "File2", "File n"]}
"webfonts" : {"Files": ["File1", "File2", "File n"]}
}
}
}
}
Is this achievable? If so, how should I go about it? I'm relatively new to perl and I'm having a hard time wrapping my head around this.
Upvotes: 2
Views: 400
Reputation: 6808
Following piece of code puts directory structure into hash and then converts it into JSON format
use strict;
use warnings;
use JSON;
my $data;
my $dir = shift // '.'; # // correct website highlighting
$data->{$dir} = traverse($dir);
my $encoder = JSON->new->ascii->pretty;
print $encoder->encode($data);
sub traverse {
my $dir = shift;
my($dh,$data);
$data->{'files'} = ();
opendir($dh, $dir)
or die "Could not open $dir";
while( my $name = readdir($dh) ) {
next if $name eq '.' or $name eq '..';
my $path = "$dir/$name";
$data->{$name} = traverse($path) if -d $path;
push @{$data->{'files'}}, $name if -f $path;
}
closedir $dh;
return $data;
}
Upvotes: 0
Reputation: 6808
Other variation of the code: files are array elements, directories are hash with key directory name and content as array, then converted to JSON format
use strict;
use warnings;
use JSON;
my $data;
my $dir = shift // '.'; # // correct website highlighting
$data->{$dir} = traverse($dir, $dir);
my $encoder = JSON->new->ascii->pretty;
print $encoder->encode($data);
sub traverse {
my $dir = shift;
my $name = shift;
my($dh,@data,%ret);
opendir($dh, $dir)
or die "Could not open $dir";
while( my $name = readdir($dh) ) {
next if $name eq '.' or $name eq '..';
my $path = "$dir/$name";
push @data, traverse($path,$name) if -d $path;
push @data, $name if -f $path;
}
closedir $dh;
return ( $name => \@data );
}
Upvotes: 0
Reputation: 3262
I gave it a try and decided to move the file names to an entry with the special key __files__
. This will break if you have a directory named like that.
use File::Find;
use JSON;
use strict;
use warnings;
use 5.12.0;
use Data::Dumper;
my $dirs={};
my $encoder = JSON->new->ascii->pretty;
find({wanted => \&process_dir, no_chdir => 1 }, ".");
print $encoder->encode($dirs);
sub process_dir {
my $full_name = $File::Find::name;
my $ref=$dirs;
if (-d $full_name ) {
for (split(/\//, $full_name)) { # only directories
$ref->{$_} ||= {}; # create the next level if it does not exist
$ref = $ref->{$_}; # move to the next level
}
} else {
for (split(/\//, $full_name)){ # n directories and 1 filename
if (exists $ref->{$_}) { # it's a directory
$ref = $ref->{$_}
} else {
push @{$ref->{__files__}}, $_; # it's a filename
# $ref->{$_} = '_is_file_';
}
}
}
}
Upvotes: 2