Page 1 of 1

Tree command for upysh

Posted: Tue Jan 07, 2020 8:41 pm
by cgglzpy
Hi I made TREE class for upysh (using LS as a template)

So doing

Code: Select all

>>> from upysh import *
Then you will be able to do this

Code: Select all

>>> tree
  ATEXTFILE.txt
  THETESTCODE.py
  my_other_dir_sync <dir>
        └────  another_file.txt
  new_tree_test_dir <dir>
        ├────  example_code.py
        ├────  foo_file.txt
        ├────  sub_foo_test_dir <dir>
        │    ├────  file_code.py
        │    └────  foo2.txt
        ├────  w_name_dir <dir>
        │    └────  dummy_file.txt
        └────  zfile.py
  test_subdir_sync <dir>
        ├────  SUBTEXT.txt
        └────  sub_sub_dir_test_sync <dir>
             ├────  level_2_subtext.txt
             └────  level_3_subtext.txt
6 directories, 12 files
The class is this one:

Code: Select all

class TREE:

    def __repr__(self):
        self.__call__()
        return ""

    def __call__(self, path=".", level=0, is_last=False, is_root=True,
                 carrier="    "):
        l = os.listdir(path)
        nf = len([file for file in os.listdir(path) if not os.stat(file)[0] & 0x4000])
        nd = len(l) - nf
        ns_f, ns_d = 0, 0
        l.sort()
        if len(l) > 0:
            last_file = l[-1]
        else:
            last_file = ''
        for f in l:
            st = os.stat("%s/%s" % (path, f))
            if st[0] & 0x4000:  # stat.S_IFDIR
                print(self._treeindent(level, f, last_file, is_last=is_last, carrier=carrier) + "  %s <dir>" % f)
                os.chdir(f)
                level += 1
                lf = last_file == f
                if level > 1:
                    if lf:
                        carrier += "     "
                    else:
                        carrier += "    │"
                ns_f, ns_d = self.__call__(level=level, is_last=lf,
                                           is_root=False, carrier=carrier)
                if level > 1:
                    carrier = carrier[:-5]
                os.chdir('..')
                level += (-1)
                nf += ns_f
                nd += ns_d
            else:
                print(self._treeindent(level, f, last_file, is_last=is_last, carrier=carrier) + "  %s" % (f))
        if is_root:
            print('{} directories, {} files'.format(nd, nf))
        else:
            return (nf, nd)

    def _treeindent(self, lev, f, lastfile, is_last=False, carrier=None):
        if lev == 0:
            return ""
        else:
            if f != lastfile:
                return carrier + "    ├────"
            else:
                return carrier + "    └────"


tree = TREE()
If anyone is interested/this is good enough I guess I could do a pull request to micropython-lib?

Cheers

Re: Tree command for upysh

Posted: Tue Jan 07, 2020 11:44 pm
by rcolistete
Nice ! Yes, please (for the pull request).

Re: Tree command for upysh

Posted: Wed Jan 08, 2020 7:49 am
by pythoncoder
That is very useful indeed, especially for those who use Python packages. :D

On a Pyboard with an SD card there is a problem: it lists the contents of .Trash-1000 which (on my SD card) are huge. It would be good if there were some way to disable this - an option to suppress hidden files/directories perhaps?

If you submit a PR I suggest you update the man command.

Re: Tree command for upysh

Posted: Wed Jan 08, 2020 7:51 am
by devnull
Nice, just included it in my custom sh file :-)

Re: Tree command for upysh

Posted: Wed Jan 08, 2020 8:23 am
by shaoziyang
It is useful.

Re: Tree command for upysh

Posted: Wed Jan 08, 2020 5:42 pm
by cgglzpy
Hi, thanks

I've just submitted the PR
pythoncoder wrote:
Wed Jan 08, 2020 7:49 am
That is very useful indeed, especially for those who use Python packages. :D

On a Pyboard with an SD card there is a problem: it lists the contents of .Trash-1000 which (on my SD card) are huge. It would be good if there were some way to disable this - an option to suppress hidden files/directories perhaps?

If you submit a PR I suggest you update the man command.
And yes , there is still room for improvement like the option to suppress hidden files/directories you mention or maybe max display depth of the directory tree for example.

That's definitely in the TODO list, and if anyone wants to contribute here is the fork https://github.com/Carglglz/micropython-lib

Cheers

Re: Tree command for upysh

Posted: Wed Jan 08, 2020 6:28 pm
by pythoncoder
On a quick test the Linux implementation of tree ignores hidden files and directories. So I suggest changing this line to:

Code: Select all

l = [x for x in os.listdir(path) if not x.startswith('.')]
That fixes the problem here.

Re: Tree command for upysh

Posted: Thu Jan 09, 2020 3:42 pm
by cgglzpy
pythoncoder wrote:
Wed Jan 08, 2020 6:28 pm
On a quick test the Linux implementation of tree ignores hidden files and directories. So I suggest changing this line to:

Code: Select all

l = [x for x in os.listdir(path) if not x.startswith('.')]
That fixes the problem here.
I came to the same conclusion yesterday after a quick look so it ignores the hidden files and directories by default (with a 'hidden' option) the class is like this now:

Code: Select all

def __call__(self, path=".", level=0, is_last=False, is_root=True,
                 carrier="    ", hidden=False):
        if hidden:
            l = os.listdir(path)
        else:
            l = [f for f in os.listdir(path) if not f.startswith('.')]
I will modify the PR then, thanks for the idea :)

Re: Tree command for upysh

Posted: Fri Jan 17, 2020 4:56 pm
by cgglzpy
Hi, I made a 'display disk usage statistics' / 'du' command too, in case anyone is interested...

It's like 'ls' command but size is in 'Human-readable' output, by default do not list hidden files/dirs, it prints also size of dirs and
it has an option for the depth level (default 0) e.g.

Code: Select all

>>> du
8.3 KB    ./test_subdir_sync <dir>
177 by    ./new_tree_test_dir <dir>
0 by      ./my_other_dir_sync <dir>
122 by    ./ATEXTFILE.txt
302 by    ./THETESTCODE.py
or

Code: Select all

>>> du(max_dlev=2)
16 by     ./test_subdir_sync/SUBTEXT.txt
42 by     ./test_subdir_sync/sub_sub_dir_test_sync/level_3_subtext.txt
8.3 KB    ./test_subdir_sync/sub_sub_dir_test_sync/level_2_subtext.txt
0 by      ./new_tree_test_dir/zfile.py
56 by     ./new_tree_test_dir/w_name_dir/dummy_file.txt
29 by     ./new_tree_test_dir/foo_file.txt
92 by     ./new_tree_test_dir/example_code.py
0 by      ./new_tree_test_dir/sub_foo_test_dir/foo2.txt
0 by      ./new_tree_test_dir/sub_foo_test_dir/file_code.py
0 by      ./my_other_dir_sync/another_file.txt
122 by    ./ATEXTFILE.txt
302 by    ./THETESTCODE.py
The class is this one

Code: Select all

class DISK_USAGE:

    def __repr__(self):
        self.__call__()
        return ""

    def __call__(self, path=".", dlev=0, max_dlev=0, hidden=False):
        if path != ".":
                if not os.stat(path)[0] & 0x4000:
                    print('{:9} {}'.format(self.print_filesys_info(os.stat(path)[6]), path))
                else:
                    if hidden:
                        resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path)}
                    else:
                        resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path) if not dir.startswith('.')}
                    for dir in resp.keys():

                        if not os.stat(dir)[0] & 0x4000:
                            print('{:9} {}'.format(self.print_filesys_info(resp[dir]), dir))

                        else:
                            if dlev < max_dlev:
                                dlev += 1
                                self.__call__(path=dir, dlev=dlev, max_dlev=max_dlev, hidden=hidden)
                                dlev += (-1)
                            else:
                                print('{:9} {} {}'.format(self.print_filesys_info(self.get_dir_size_recursive(dir)), dir, '<dir>'))
        else:
            if hidden:
                resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path)}
            else:
                resp = {path+'/'+dir: os.stat(path+'/'+dir)[6] for dir in os.listdir(path) if not dir.startswith('.')}
            for dir in resp.keys():

                if not os.stat(dir)[0] & 0x4000:
                    print('{:9} {}'.format(self.print_filesys_info(resp[dir]), dir))

                else:
                    if dlev < max_dlev:
                        dlev += 1
                        self.__call__(path=dir, dlev=dlev, max_dlev=max_dlev, hidden=hidden)
                        dlev += (-1)
                    else:
                        print('{:9} {} {}'.format(self.print_filesys_info(self.get_dir_size_recursive(dir)), dir, '<dir>'))

    def print_filesys_info(self, filesize):
        _kB = 1024
        if filesize < _kB:
            sizestr = str(filesize) + " by"
        elif filesize < _kB**2:
            sizestr = "%0.1f KB" % (filesize / _kB)
        elif filesize < _kB**3:
            sizestr = "%0.1f MB" % (filesize / _kB**2)
        else:
            sizestr = "%0.1f GB" % (filesize / _kB**3)
        return sizestr

    def get_dir_size_recursive(self, dir):
        return sum([os.stat(dir+'/'+f)[6] if not os.stat(dir+'/'+f)[0] & 0x4000 else self.get_dir_size_recursive(dir+'/'+f) for f in os.listdir(dir)])
Cheers