2017年4月15日土曜日

tree コマンドの作成

ubuntu などでは最初からインストールされないコマンドで使いたくなるのが tree コマンドである。 たが、開発環境の制約から自由にプログラムを追加できない場合も多い。



そんな場合に利用可能な言語で自前のツールとして用意しておくと助かる場合もある。以下は、シンボリックリンクや日本語のソート(sjisコード順)には対応せず、単純なソートと再帰呼び出しの組み合わせである。

  • perl で tree コマンド作成
  • ruby で tree コマンド作成
  • python で tree コマンド作成
  • clisp で tree コマンド作成
  • java で tree コマンド作成
  • C言語 で tree コマンド作成
  • 速度比較

    1.perl で tree コマンド作成


    #! /usr/bin/perl
    use utf8;
    #use Encode qw/encode decode/;
    use Encode;
    
    if (scalar(@ARGV) == 0) {
      $root_dir = "."
    }
    else {
      $root_dir = shift(@ARGV);
    }
    printf("%s\n",$root_dir);
    &tree("",$root_dir);
    
    sub tree {
      my($depth,$dir) = @_;
    
      if (!opendir(FD,$dir)) {
        printf("Can't open dir[%s]\n",$dir);
        exit(1);
      }
    
      my(@list) = ();
      foreach $file (readdir(FD)) {
        if ($file =~ /^[.]$/ || $file =~ /^[.][.]$/) {
          next;
        }
    #    $file = decode('UTF-8', $file);
    #    $file = encode('Shift_JIS', $file);
        push(@list,$file)
      }
      close(fd);
    
      my($cnt) = 0;
      foreach $file (sort @list) {
        $cnt++;
    
    #    $file = decode('Shift_JIS', $file);
    #    $file = encode('UTF-8', $file);
        my($fullpath) = $dir . "/" . $file;
        if ( -d $fullpath ) {
          if (scalar(@list) == $cnt) {
            printf("%s`-- %s\n",$depth,$file);
            tree($depth . "    ",$fullpath);
          }
          else {
            printf("%s|-- %s\n",$depth,$file);
            tree($depth . "|   ",$fullpath);
          }
        }
        else {
          if (scalar(@list) == $cnt) {
            printf("%s`-- %s\n",$depth,$file);
          }
          else {
            printf("%s|-- %s\n",$depth,$file);
          }
        }
      }
    }
    

    2.ruby で tree コマンド作成


    #! /usr/bin/ruby
    
    def tree(depth,dir)
      f =  Dir.open(dir);
      list = []
      f.each{ |file|
        if (file =~ /^[.]$/ || file =~ /^[.][.]$/)
          next
        end
    #    file = file.encode("Shift_JIS", :undef => :replace, :replace => "*")
        list.push(file)
      }
    
      cnt = 0
      list.sort.each {|file|
    #    file = file.encode("UTF-8")
        cnt = cnt +1
    
        fullpath = dir + "/" + file
    
        if (File.ftype(fullpath) == "directory")
          if (list.length == cnt)
            printf("%s`-- %s\n",depth,file)
            tree(depth + "    ",fullpath)
          else
            printf("%s|-- %s\n",depth,file)
            tree(depth + "|   ",fullpath)
          end
        else
          if (list.length == cnt)
            printf("%s`-- %s\n",depth,file)
          else
            printf("%s|-- %s\n",depth,file)
          end
        end
     }
    end
    
    if (ARGV.size() == 0)
      root_dir = "."
    else
      root_dir = ARGV[0]
    end
    printf("%s\n",root_dir)
    tree("",root_dir)
    

    3.python で tree コマンド作成


    #! /usr/bin/python
    
    import os
    import sys
    
    def tree(depth,dir):
      files = sorted(os.listdir(dir))
      cnt = 0
    
      for file in files:
        fullpath =  dir + "/" + file
        cnt = cnt + 1
    
        if os.path.isdir(fullpath):
          if len(files) == cnt:
            print "%s`-- %s"%(depth,file)
            tree(depth + "    " ,fullpath)
          else:
            print "%s|-- %s"%(depth,file)
            tree(depth + "|   " ,fullpath)
        else:
          if len(files) == cnt:
            print "%s`-- %s"%(depth,file)
          else:
            print "%s|-- %s"%(depth,file)
    
    root_dir = "."
    if len(sys.argv) > 1:
      root_dir = sys.argv[1]
    
    print root_dir
    tree("",root_dir)
    

    4.clisp で tree コマンド作成


    #! /usr/bin/clisp
    
    (defun fname-chop(tfile)
      ;;
      (let ((buf (namestring tfile)))
           (setq ch (subseq buf (- (length buf) 1) (length buf)))
           (if (equal ch "/")
               (subseq buf 0 (- (length buf) 1))
               (subseq buf 0 (length buf)))))
    
    (defun tree(depth dir)
      
      (let (
        (list (concatenate 
               'list
               (directory (concatenate 'String dir "*/"))
               (directory (concatenate 'String dir "*"))))
        (tmp "")
        (cnt 0))
    
        (sort list #'string< :key #'fname-chop)
    
        (dolist (file list)
          (progn 
            (incf cnt)
            (setq tfile (namestring file))
            (setq tfile (subseq tfile 0 (- (length tfile) 1)))
    
            (if (equal (file-namestring file) "")
              (if (equal (length list) cnt)
                (progn 
                  (format t "~A`-- ~A~%" depth (file-namestring tfile))
                  (tree (concatenate 'String depth "    ") (namestring file))
                  )
                (progn 
                  (format t "~A|-- ~A~%" depth (file-namestring tfile))
                  (tree (concatenate 'String depth "|   ") (namestring file))
                  ))
              (if (equal (length list) cnt)
                (progn 
                  (format t "~A`-- ~A~%" depth (file-namestring file))
                  )
                (progn 
                  (format t "~A|-- ~A~%" depth (file-namestring file))
                  )))))))
    
    (let 
      ((root_dir ""))
      (if (equal (length *args*) 0)
        (setq root_dir "./")
        (setq root_dir (car *args*)))
      (if (not (equal (subseq root_dir (- (length root_dir) 1) (length root_dir)) "/")) 
        (format t "test01 [dir]"))
      (format t "~A~%" root_dir)
      (tree "" root_dir))
    

    5.java で tree コマンド作成


    import java.io.*;
    import java.util.*;
    
    class tree {
      void tree(String depth,File dir) {
        File[] files = dir.listFiles();
        int cnt = 0; 
    
        Arrays.sort(files);
    
        if (files == null) {
          return;
        }
        for(File file: files) {
          cnt++;
          if (file.isDirectory()) {
            if (files.length == cnt) {
              System.out.printf("%s`-- %s\n",depth,file.getName());
              tree(depth + "    ",new File(file.getAbsolutePath())); 
            }
            else {
              System.out.printf("%s|-- %s\n",depth,file.getName());
              tree(depth + "|   ",new File(file.getAbsolutePath())); 
            }
          }
          else {
            if (files.length == cnt) {
              System.out.printf("%s`-- %s\n",depth,file.getName());
            }
            else {
              System.out.printf("%s|-- %s\n",depth,file.getName());
            }
          }
        }
      }
    
      public static void main(String args[]) {
        String root_dir = ".";
    
        if (args.length != 0) {
          root_dir = args[0];
        }
        System.out.printf("%s\n",root_dir);
    
        tree obj = new tree();
        obj.tree("",new File(root_dir));
      }
    }
    

    6.C言語 で tree コマンド作成


    #include <stdio.h>
    #include <stdlib.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <dirent.h>
    #include <string.h>
    #include <unistd.h>
    
    typedef struct dirent DENT;
    typedef struct {
      int   cnt;
      DENT *list;
    } DENT_B;
    
    int
    comp(src,dst) 
    DENT *src;
    DENT *dst;
    {
        return(strcmp(src->d_name,dst->d_name));
    }
    
    int
    list_alloc(base)
    DENT_B *base;
    {
        if (!base->list) {
            base->list = malloc(sizeof(DENT)*base->cnt);
            if (!base->list) {
                return(1);
            }
        }
        else {
            base->list = realloc(base->list, sizeof(DENT)*base->cnt);
        } 
    
        return(0);
    }
    
    int
    mklist(base,dir) 
    DENT_B *base;
    char   *dir;
    {
        DIR  *dp   = NULL;
        int   cnt  = 0;
        DENT *dent = NULL;
    
        dp=opendir(dir);
        if (!dp) {
            fprintf(stderr,"Can't open directory(%s)\n",dir);
            return(1);
        }
    
        base->cnt = 0;
        while(dent = readdir(dp)) {
            if (dent == (struct dirent *)-1) {
                fprintf(stderr,"Can't read directory(%s)\n",dir);
                return(1);
            }
    
            if (!strcmp(dent->d_name,".") ||
                !strcmp(dent->d_name,"..")) {
                continue;
            }
            
            base->cnt++;
            if (list_alloc(base)) {
                fprintf(stderr,"Can't alloc memory\n");
                return(1);
            }
    
            memcpy(&base->list[base->cnt-1],dent,sizeof(DENT));
        }
    
        if (closedir(dp)) {
            fprintf(stderr,"Can't close directory(%s)\n",dir);
            return(1);
        }
    
        return(0);
    }
    
    int
    tree(depth, dir)
    char *depth;
    char *dir;
    {
        DENT_B  base;
        int     cnt;
        char    fullpath[512];
        char    depth_tmp[512];
        struct stat dstat;
    
        memset(&base,0x00,sizeof(base));
    
        if (mklist(&base,dir)) {
            fprintf(stderr,"mklist error(%s)\n",dir);
            return(1);
        }
    
        qsort(base.list,base.cnt,sizeof(DENT),comp);
    
        for(cnt=0;cnt<base.cnt;cnt++) {
            sprintf(fullpath,"%s/%s",dir,base.list[cnt].d_name);
            if (stat(fullpath,&dstat)) {
                fprintf(stderr,"stat error(%s)\n",fullpath);
                return(1);
            }
            if (dstat.st_mode & S_IFDIR) {
                if (cnt+1 == base.cnt) {
                    printf("%s`-- %s\n",depth,base.list[cnt].d_name);
                    sprintf(depth_tmp,"%s    ",depth);
                    tree(depth_tmp,fullpath);
                }
                else {
                    printf("%s|-- %s\n",depth,base.list[cnt].d_name);
                    sprintf(depth_tmp,"%s|   ",depth);
                    tree(depth_tmp,fullpath);
                }
            }
            else {
                if (cnt+1 == base.cnt) {
                    printf("%s`-- %s\n",depth,base.list[cnt].d_name);
                }
                else {
                    printf("%s|-- %s\n",depth,base.list[cnt].d_name);
                }
            }
        }
    
        free(base.list);
    
        return(0);
    }
    
    int
    main(argc,argv)
    int argc;
    char *argv[];
    {
        char *root_dir = NULL;
    
        root_dir = ".";
        if (argc > 1) {
            root_dir = argv[1];
        }
        printf("%s\n",root_dir);
        if (tree("",root_dir)!=0) {
           exit(1);
        }
        exit(0);
    }
    

    7.速度比較(2455 directories, 22931 files)


    == perl ==
    
    real    0m0.202s
    user    0m0.112s
    sys     0m0.088s
    
    == ruby ==
    
    real    0m0.333s
    user    0m0.196s
    sys     0m0.112s
    
    == python ==
    
    real    0m0.234s
    user    0m0.116s
    sys     0m0.112s
    
    == lisp ==
    
    real    0m2.564s
    user    0m1.736s
    sys     0m0.792s
    
    == java ==
    
    real    0m1.050s
    user    0m1.472s
    sys     0m0.432s
    
    == C ==
    
    real    0m0.101s
    user    0m0.020s
    sys     0m0.080s
    
    

  • 0 件のコメント:

    コメントを投稿