19

I want to use tree (or similar) to see the directory structure of a given directory as well as whether each subdirectory has files in it. So, how could I use tree but limit the maximum number of files to display in a given subdirectory?

If it can't be done with tree, how could it be done by modifying the Python code from this site?

4 Answers 4

8
+50

Here's a working example with the python code you cited:

Usage: tree.py -f [file limit] <directory>

If a number is specified for -f [file limit] then ... <additional files> is printed and the other files are skipped. Additional directories should not be skipped however. If the file limit is set to 10000 (default) this acts as no limit

#! /usr/bin/env python
# tree.py
#
# Written by Doug Dahms
# modified by glallen @ StackExchange
#
# Prints the tree structure for the path specified on the command line

from os import listdir, sep
from os.path import abspath, basename, isdir
from sys import argv

def tree(dir, padding, print_files=False, limit=10000):
    print padding[:-1] + '+-' + basename(abspath(dir)) + '/'
    padding = padding + ' '
    limit = int(limit)
    files = []
    if print_files:
        files = listdir(dir)
    else:
        files = [x for x in listdir(dir) if isdir(dir + sep + x)]
    count = 0
    for file in files:
        count += 1
        path = dir + sep + file
        if isdir(path):
            print padding + '|'
            if count == len(files):
                tree(path, padding + ' ', print_files, limit)
            else:
                tree(path, padding + '|', print_files, limit)
        else:
            if limit == 10000:
                print padding + '|'
                print padding + '+-' + file
                continue
            elif limit == 0:
                print padding + '|'
                print padding + '+-' + '... <additional files>'
                limit -= 1
            elif limit <= 0:
                continue
            else:
                print padding + '|'
                print padding + '+-' + file
                limit -= 1

def usage():
    return '''Usage: %s [-f] [file-listing-limit(int)] <PATH>
Print tree structure of path specified.
Options:
-f          Print files as well as directories
-f [limit]  Print files as well as directories up to number limit
PATH        Path to process''' % basename(argv[0])

def main():
    if len(argv) == 1:
        print usage()
    elif len(argv) == 2:
        # print just directories
        path = argv[1]
        if isdir(path):
            tree(path, ' ')
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    elif len(argv) == 3 and argv[1] == '-f':
        # print directories and files
        path = argv[2]
        if isdir(path):
            tree(path, ' ', True)
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    elif len(argv) == 4 and argv[1] == '-f':
        # print directories and files up to max
        path = argv[3]
        if isdir(path):
            tree(path, ' ', True, argv[2])
        else:
            print 'ERROR: \'' + path + '\' is not a directory'
    else:
        print usage()

if __name__ == '__main__':
    main()

When run, it should produce output similar to:

user@host /usr/share/doc $ python /tmp/recipe-217212-1.py -f 2 . | head -n 40
+-doc/
  |
  +-libgnuradio-fft3.7.2.1/
  | |
  | +-copyright
  | |
  | +-changelog.Debian.gz
  |
  +-libqt4-script/
  | |
  | +-LGPL_EXCEPTION.txt
  | |
  | +-copyright
  | |
  | +-... <additional files>
  |
  +-xscreensaver-gl/
  | |
  | +-copyright
  | |
  | +-changelog.Debian.gz
  | |
  | +-... <additional files>
2
  • This will be nice if the option --filelimit is included in native Unix/Linux tree command.
    – sugab
    Jun 17, 2019 at 8:37
  • 1
    @sugab --filelimit exists in the current version of tree (v2.1.1, 3/2024) Mar 21 at 16:25
9

One can use tree --filelimit=N to limit number of subdirectories/file to display. Unfortunately, this will not open directory which has more than N sub-directories and files.

For simple cases, when you have multiple directories and most have too many(say > 100) files, you can use tree --filelimit=100.

.
├── A1
│   ├── A2
│   ├── B2
│   ├── C2 [369 entries exceeds filelimit, not opening dir]
│   └── D2 [3976 entries exceeds filelimit, not opening dir]
├── B1
│   └── A2
│       ├── A3.jpeg
│       └── B3.png
└── C1.sh

Note, if A1/C2 has a sub-directory A3, it will not be shown.

P.S. This is not a complete solution but will be quicker for few.

2
  • 7
    4 years later, searching for my own answer :D Oct 11, 2021 at 12:54
  • Is it possible to avoid this limit on the called directory? If I call tree on a specific directory, I usually want standard tree behavior (or at least some information) rather than a blanket refusal. For example: $ tree . --filelimit 10 . [59 entries exceeds filelimit, not opening dir] Mar 21 at 15:48
0

Updated Python 3 version of @glallen's tree.py above.

#!/usr/bin/env python3
# tree.py
#
# Written by Doug Dahms
# modified by glallen @ StackExchange
#
# Prints the tree structure for the path specified on the command line

import os
import sys

def tree(directory, padding, print_files=False, limit=10000):
    print(padding[:-1] + '+-' + os.path.basename(os.path.abspath(directory)) + '/')
    padding = padding + ' '
    limit = int(limit)
    files = []
    if print_files:
        files = os.listdir(directory)
    else:
        files = [x for x in os.listdir(directory) if os.path.isdir(os.path.join(directory, x))]
    count = 0
    for file in files:
        count += 1
        path = os.path.join(directory, file)
        if os.path.isdir(path):
            print(padding + '|')
            if count == len(files):
                tree(path, padding + ' ', print_files, limit)
            else:
                tree(path, padding + '|', print_files, limit)
        else:
            if limit == 10000:
                print(padding + '|')
                print(padding + '+-' + file)
                continue
            elif limit == 0:
                print(padding + '|')
                print(padding + '+-' + '... <additional files>')
                limit -= 1
            elif limit <= 0:
                continue
            else:
                print(padding + '|')
                print(padding + '+-' + file)
                limit -= 1

def usage():
    return '''Usage: {} [-f] [file-listing-limit(int)] <PATH>
Print tree structure of path specified.
Options:
-f          Print files as well as directories
-f [limit]  Print files as well as directories up to number limit
PATH        Path to process'''.format(os.path.basename(sys.argv[0]))

def main():
    if len(sys.argv) == 1:
        print(usage())
    elif len(sys.argv) == 2:
        # print just directories
        path = sys.argv[1]
        if os.path.isdir(path):
            tree(path, ' ')
        else:
            print('ERROR: \'' + path + '\' is not a directory')
    elif len(sys.argv) == 3 and sys.argv[1] == '-f':
        # print directories and files
        path = sys.argv[2]
        if os.path.isdir(path):
            tree(path, ' ', True)
        else:
            print('ERROR: \'' + path + '\' is not a directory')
    elif len(sys.argv) == 4 and sys.argv[1] == '-f':
        # print directories and files up to max
        path = sys.argv[3]
        if os.path.isdir(path):
            tree(path, ' ', True, sys.argv[2])
        else:
            print('ERROR: \'' + path + '\' is not a directory')
    else:
        print(usage())

if __name__ == '__main__':
    main()
0

Another approach may be to filter the json output provided by tree. For example, tree -J displays:

[
  {"type":"directory","name":"root_directory/","contents":[
    {"type":"directory","name":"child_directory","contents":[
      {"type":"file","name":"file06.txt"},
      {"type":"file","name":"file07.txt"}
    ]},
    {"type":"file","name":"file00.txt"},
    {"type":"file","name":"file01.txt"},
    {"type":"file","name":"file02.txt"},
    {"type":"file","name":"file03.txt"},
    {"type":"file","name":"file04.txt"},
    {"type":"file","name":"file05.txt"}
  ]},
  {"type":"report","directories":2,"files":8}
]

This json can be filtered to truncate long lists of files.

import json

def truncate_directories(json_data, max_files):
    # Parse JSON data
    data = json.loads(json_data)

    # Iterate through each item in the JSON data
    for item in data:
        if item.get('type') == 'directory':
            contents = item.get('contents')
            if contents and len(contents) > max_files:
                # Truncate the contents of the directory
                item['contents'] = contents[:3] + [{"type": "file", "name": "..."}] + contents[-3:]

    # Convert the modified data back to JSON format
    return json.dumps(data, indent=2)

# Example JSON data
json_data = '''
[
  {"type":"directory","name":"root_directory/","contents":[
    {"type":"directory","name":"child_directory","contents":[
      {"type":"file","name":"file06.txt"},
      {"type":"file","name":"file07.txt"}
    ]},
    {"type":"file","name":"file00.txt"},
    {"type":"file","name":"file01.txt"},
    {"type":"file","name":"file02.txt"},
    {"type":"file","name":"file03.txt"},
    {"type":"file","name":"file04.txt"},
    {"type":"file","name":"file05.txt"}
  ]},
  {"type":"report","directories":2,"files":8}
]
'''

# Set the maximum number of files allowed in a directory
max_files = 3

# Truncate directories with too many files
new_json_data = truncate_directories(json_data, max_files)

# Print the modified JSON data
print(new_json_data)

[
  {
    "type": "directory",
    "name": "root_directory/",
    "contents": [
      {
        "type": "directory",
        "name": "child_directory",
        "contents": [
          {"type": "file", "name": "file06.txt"},
          {"type": "file", "name": "file07.txt"}
        ]
      },
      {"type": "file", "name": "file00.txt"},
      {"type": "file", "name": "..."},             # TRUNCATED FILES
      {"type": "file", "name": "file05.txt"}
    ]
  },
  {"type": "report", "directories": 2, "files": 8}
]

In the simulated output, the "root_directory/" contains six files instead of eight. The middle files "file01.txt", "file02.txt", "file03.txt" and "file04.txt" have been replaced with {"type": "file", "name": "..."} to meet the truncation criteria.

If this json is properly output (e.g., as paths or tab indented files), 'tree' should be able to display the custom truncated tree data.

$ tree --help

------- Input options -------
-fromfile    Reads paths from files (.=stdin)
--fromtabfile Reads trees from tab indented files (.=stdin)

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .