Text-Processing

將 tree 命令的輸出轉換為 json 格式

  • February 16, 2022

有沒有一種方便的方法可以將 *nix 命令“tree”的輸出轉換為 JSON 格式?

編輯: 我認為我沒有很好地描述我的問題。我的目標是轉換如下內容:

.
|-- dir1
|   |-- dirA
|   |   |-- dirAA
|   |   `-- dirBB
|   `-- dirB
`-- dir2
   |-- dirA
   `-- dirB

進入:

{"dir1" : [{"dirA":["dirAA", "dirAB"]}, "dirB"], "dir2": ["dirA", "dirB"]}

嘗試 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::Findmodule 的工作方式與 unixfind命令類似。該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"
        ]
     }
  ]
}

我發現這非常棘手(特別是考慮到“如果子目錄雜湊,如果不是數組,哦,除非頂層,然後只是雜湊”邏輯)。所以如果這是你可以用sed/做的事情,我會感到驚訝awk……但是斯蒂芬還沒有看過這個我敢打賭:)

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}
]

引用自:https://unix.stackexchange.com/questions/90115