3

I am trying to build the structure like this.

{
  "file1": "supersong.mp3",
  "file2": "supersong2.mp3",
  "file3": "text.txt",
  "file4": "tex2t.txt",
  "file5": "text3.txt",
  "file6": "json.pl",
  "directory_movies": [ 
      "file1": "supersong.mp3",
      "file2": "supersong2.mp3",
      "file3": "text.txt",
      "file4": "tex2t.txt",
      "file5": "text3.txt",
      "file6": "json.pl",
      "directory_sub_movies": [ 
           "file1": "supersong.mp3",
           "file2": "supersong2.mp3",
           "file3": "text.txt",
           "file4": "tex2t.txt",
           "file5": "text3.txt",
           "file6": "json.pl",
  ]

] };

So as any directory hierarchy in my case in unix. So we have simple files or directories, if it is directory it is nested hash and so on recursively.
I need to represent it as hash in perl, the easiest way I have found is to use File::Find module.
It works correctly but I cannot figure out how to save hierarchy in hash to be nested as above.
Here is my test script. That determines type of current item correctly.

sub path_checker {
    if (-d $File::Find::name) {
        print "Directory " . $_ . "\n";   
    }
    elsif (-f $File::Find::name) {
        print "File " . $_ . " Category is " . basename($File::Find::dir) . "\n";  
    }

}
sub parse_tree {
    my ($class,$root_path) = @_;
    File::Find::find(\&path_checker, $root_path);
}

Please help to modify it to create structure like I have described above. I would be very grateful.

danoaqfd
  • 53
  • 5

1 Answers1

4

Subfolders should also be hashes, not arrays,

use strict;
use warnings;

# use Data::Dumper;
use File::Find;
use JSON;

sub parse_tree {
    my ($root_path) = @_;

    my %root;
    my %dl;
    my %count;

    my $path_checker = sub {
      my $name = $File::Find::name;
      if (-d $name) {
        my $r = \%root;
        my $tmp = $name;
        $tmp =~ s|^\Q$root_path\E/?||;
        $r = $r->{$_} ||= {} for split m|/|, $tmp; #/
        $dl{$name} ||= $r;
      }
      elsif (-f $name) {
        my $dir = $File::Find::dir;
        my $key = "file". ++$count{ $dir };
        $dl{$dir}{$key} = $_;
      }
    };
    find($path_checker, $root_path);

    return \%root;
}

print encode_json(parse_tree("/tmp"));
mpapec
  • 50,217
  • 8
  • 67
  • 127
  • 1
    Thank you !Works like a charm. Spasibo) – danoaqfd Jul 09 '15 at 20:45
  • 1
    @danoaqfd you can use `print Dumper \%dl, \%count` to check inner workings. – mpapec Jul 09 '15 at 20:48
  • Sorry for disturbing again, but I have just noticed that it doesn't recursively prints directory content, only one level and subdirectories are subdirectory":{} , but it has files and sub directories. – danoaqfd Jul 10 '15 at 05:44
  • @danoaqfd do you as a user have sufficient privileges to scan these folders? Ie `ls -laR /etc` – mpapec Jul 10 '15 at 05:57
  • @Сухой27 How could we limit depth here easily? Thank you very much for your answer. – Ilia Ross Mar 16 '18 at 09:02
  • 1
    @IliaRostovtsev you could check for case when it is `-d $name` and `$name` having more slashes (depth) than limit allows, ie. https://stackoverflow.com/questions/9919909/when-using-perls-filefind-whats-a-quick-easy-way-to-limit-search-depth – mpapec Mar 16 '18 at 11:05
  • 1
    @Сухой27 Thanks a lot. – Ilia Ross Mar 16 '18 at 12:10
  • @Сухой27 Could you please join https://chat.stackoverflow.com/rooms/167012/perl-tricks? I have few questions for you. I'm stuck. It will not take long, I promise. Спасибо! :) – Ilia Ross Mar 17 '18 at 12:48