Вопрос или проблема
Я хочу использовать tree
(или аналог) для просмотра структуры каталога и наличия файлов в каждом подкаталоге. Итак, как я могу использовать tree
, но ограничить максимальное количество файлов для отображения в данном подкаталоге?
Если это невозможно с tree
, как это можно сделать, изменив код на Python с этого сайта?
Вот рабочий пример с кодом на Python, который вы упомянули:
Использование: tree.py -f [file limit] <directory>
Если для -f [file limit] указано число, то выводится ... <additional files>
, а остальные файлы пропускаются. Дополнительные каталоги, однако, не должны пропускаться. Если лимит файлов установлен на 10000 (по умолчанию), это действует как отсутствие лимита
#! /usr/bin/env python
# tree.py
#
# Автор: Doug Dahms
# изменено: glallen @ StackExchange
#
# Выводит структуру дерева для пути, указанного в командной строке
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)) + "https://superuser.com/"
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 '''Использование: %s [-f] [file-listing-limit(int)] <PATH>
Выводит структуру дерева для указанного пути.
Опции:
-f Выводить файлы вместе с каталогами
-f [limit] Выводить файлы вместе с каталогами до числа limit
PATH Обрабатываемый путь''' % basename(argv[0])
def main():
if len(argv) == 1:
print usage()
elif len(argv) == 2:
# выводить только каталоги
path = argv[1]
if isdir(path):
tree(path, ' ')
else:
print 'ОШИБКА: \'' + path + '\' не является каталогом'
elif len(argv) == 3 and argv[1] == '-f':
# выводить каталоги и файлы
path = argv[2]
if isdir(path):
tree(path, ' ', True)
else:
print 'ОШИБКА: \'' + path + '\' не является каталогом'
elif len(argv) == 4 and argv[1] == '-f':
# выводить каталоги и файлы до максимума
path = argv[3]
if isdir(path):
tree(path, ' ', True, argv[2])
else:
print 'ОШИБКА: \'' + path + '\' не является каталогом'
else:
print usage()
if __name__ == '__main__':
main()
Когда запускается, должно получиться подобное выходное сообщение:
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>
Можно использовать tree --filelimit=N
, чтобы ограничить количество подкаталогов/файлов для отображения. К сожалению, это не откроет каталог, который имеет более чем N подкаталогов и файлов.
Для простых случаев, когда у вас несколько каталогов и большинство из них имеют слишком много (например, > 100) файлов, вы можете использовать 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
Примечание, если A1/C2 имеет подкаталог A3, он не будет показан.
P.S. Это не полное решение, но будет быстрее для нескольких.
Другой подход может заключаться в фильтрации json вывода, предоставляемого tree
. Например, tree -J
отображает:
[
{"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}
]
Этот json можно отфильтровать, чтобы усечь длинные списки файлов.
import json
def truncate_directories(json_data, max_files):
# Разбираем JSON данные
data = json.loads(json_data)
# Проходим по каждому элементу в JSON данных
for item in data:
if item.get('type') == 'directory':
contents = item.get('contents')
if contents and len(contents) > max_files:
# Усекаем содержимое директории
item['contents'] = contents[:3] + [{"type": "file", "name": "..."}] + contents[-3:]
# Преобразуем модифицированные данные обратно в JSON формат
return json.dumps(data, indent=2)
# Пример JSON данных
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}
]
'''
# Устанавливаем максимальное количество файлов, разрешенных в каталоге
max_files = 3
# Усекать каталоги с чрезмерным количеством файлов
new_json_data = truncate_directories(json_data, max_files)
# Печать модифицированных JSON данных
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": "..."}, # УСЕЧЕННЫЕ ФАЙЛЫ
{"type": "file", "name": "file05.txt"}
]
},
{"type": "report", "directories": 2, "files": 8}
]
В имитированном выводе “root_directory/” содержит шесть файлов вместо восьми. Средние файлы “file01.txt”, “file02.txt”, “file03.txt” и “file04.txt” были заменены {“type”: “file”, “name”: “…”} для соответствия критерию усечения.
Если этот JSON правильно выведен (например, в виде путей или файлов с табуляцией), ‘tree’ должен быть в состоянии отобразить данные пользовательского усеченного дерева.
$ tree --help
------- Вводные опции -------
-fromfile Читает пути из файлов (.=stdin)
--fromtabfile Читает деревья из файлов с табуляцией (.=stdin)
Внесено несколько улучшений в код @glallen:
- форматирование похоже на команду
tree
- по умолчанию используется текущий рабочий каталог
- игнорировать файлы, начинающиеся с
.
по умолчанию - обновлено до Python 3
- отформатирован код
Пример вывода:
tree.py -f 5
└── bq_labeled_patents
├── extracted_data_EN.csv
├── phase_0
│ ├── espacenet_en1.pdf
│ ├── espacenet_en10.pdf
│ ├── espacenet_en100.pdf
│ ├── espacenet_en11.pdf
│ ├── espacenet_en12.pdf
│ ├── ... <additional files>
├── phase_0.json
└── phase_1
├── eu
│ ├── espacenet_en1.pdf
│ ├── espacenet_en10.pdf
│ ├── espacenet_en100.pdf
│ ├── espacenet_en11.pdf
│ ├── espacenet_en12.pdf
│ ├── ... <additional files>
├── eu.json
├── us
│ ├── us_001.pdf
│ ├── us_002.pdf
│ ├── us_003.pdf
│ ├── us_004.pdf
│ ├── us_005.pdf
│ ├── ... <additional files>
└── us.json
Обновленный код:
#! /usr/bin/env python
# tree.py
#
# Автор: Doug Dahms
# изменено: glallen @ StackExchange
# изменено: kym @ StackExchange
# https://superuser.com/q/840152/992568
import argparse
import os
from os import listdir, sep
from os.path import abspath, basename, isdir
from sys import argv
def is_hidden(file):
return file.startswith(".") or file == "__pycache__"
def tree(
dir,
padding,
print_files=False,
limit=10000,
is_last=True,
level=0,
ignore_hidden=True,
):
basename_dir = basename(abspath(dir))
connector = "└── " if is_last else "├── "
print(padding + connector + basename_dir)
padding = padding + (" " if is_last else "│ ")
files = []
if print_files:
files = listdir(dir)
else:
files = [x for x in listdir(dir) if isdir(dir + sep + x)]
if ignore_hidden:
files = [f for f in files if not is_hidden(f)]
files = sorted(files)
total_files = len(files)
file_count = 0
for i, file in enumerate(files):
path = dir + sep + file
if isdir(path):
tree(
path,
padding,
print_files,
limit,
is_last=(i == total_files - 1),
level=level + 1,
ignore_hidden=ignore_hidden,
)
else:
file_count += 1
if file_count <= limit:
connector = "└── " if i == total_files - 1 else "├── "
print(padding + connector + file)
elif file_count == limit + 1:
connector = "└── " if i == total_files - 1 else "├── "
print(padding + connector + "... <additional files>")
def main():
parser = argparse.ArgumentParser(
description="Выводит структуру дерева для указанного пути."
)
parser.add_argument(
"path",
nargs="?",
default=os.getcwd(),
help="Обрабатываемый путь (по умолчанию: текущая директория)",
)
parser.add_argument(
"-f",
"--files",
nargs="?",
const=10000,
type=int,
help="Выводить файлы вместе с каталогами до числа limit",
)
parser.add_argument(
"--show-hidden",
action="store_true",
help="Показывать скрытые файлы (по умолчанию: скрытые файлы игнорируются)",
)
args = parser.parse_args()
path = args.path
if isdir(path):
if args.files is not None:
tree(path, " ", True, args.files, ignore_hidden=not args.show_hidden)
else:
tree(path, " ", ignore_hidden=not args.show_hidden)
else:
print("ОШИБКА: '" + path + "' не является каталогом")
if __name__ == "__main__":
main()
Обновленная версия Python 3 вышеупомянутого tree.py
от @glallen.
#!/usr/bin/env python3
# tree.py
#
# Автор: Doug Dahms
# изменено: glallen @ StackExchange
#
# Выводит структуру дерева для пути, указанного в командной строке
import os
import sys
def tree(directory, padding, print_files=False, limit=10000):
print(padding[:-1] + '+-' + os.path.basename(os.path.abspath(directory)) + "https://superuser.com/")
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 '''Использование: {} [-f] [file-listing-limit(int)] <PATH>
Выводит структуру дерева для указанного пути.
Опции:
-f Выводить файлы вместе с каталогами
-f [limit] Выводить файлы вместе с каталогами до числа limit
PATH Обрабатываемый путь'''.format(os.path.basename(sys.argv[0]))
def main():
if len(sys.argv) == 1:
print(usage())
elif len(sys.argv) == 2:
# выводить только каталоги
path = sys.argv[1]
if os.path.isdir(path):
tree(path, ' ')
else:
print('ОШИБКА: \'' + path + '\' не является каталогом')
elif len(sys.argv) == 3 and sys.argv[1] == '-f':
# выводить каталоги и файлы
path = sys.argv[2]
if os.path.isdir(path):
tree(path, ' ', True)
else:
print('ОШИБКА: \'' + path + '\' не является каталогом')
elif len(sys.argv) == 4 and sys.argv[1] == '-f':
# выводить каталоги и файлы до максимума
path = sys.argv[3]
if os.path.isdir(path):
tree(path, ' ', True, sys.argv[2])
else:
print('ОШИБКА: \'' + path + '\' не является каталогом')
else:
print(usage())
if __name__ == '__main__':
main()
Если кто-то пришел сюда через google, как я, можно также просто использовать awk:
tree -L 3 | awk '
BEGIN {
last_prefix = ""
count = 1
}
{
# Извлекаем отступ
prefix = $0
sub(/[^│├└─]+$/, "", prefix)
if (count < 5) {
print
}
if (prefix == last_prefix) {
count++
} else {
if (count > 5) {
print prefix " " count " more files..."
}
count = 1
}
last_prefix = prefix
}'
Пример вывода:
├── venv-metal
│ ├── bin
│ │ ├── Activate.ps1
│ │ ├── activate
│ │ ├── activate.csh
│ │ ├── activate.fish
│ │ ├── estimator_ckpt_converter
│ │ └── 28 more files... <<<<<<<<<<<
│ ├── include
│ │ └── python3.11
│ ├── lib
│ │ └── python3.11
│ └── pyvenv.cfg
https://gist.github.com/TTy32/4b66351dde364af54b01ca7a4dd7df02
Ответ или решение
Определение и понимание структуры файловой системы – это важный аспект для технического специалиста, работающего с операционными системами на базе Linux. Одним из популярных инструментов для отображения структуры директорий является команда tree
. Эта команда предоставляет визуальное представление иерархического расположения файлов и подпапок от указанной директории. Однако у tree
есть свои ограничения, когда возникает необходимость ограничить количество отображаемых файлов в каждой поддиректории, и в некоторых случаях стандартного функционала tree
становится недостаточно.
Теория
Команда tree
является мощным инструментом для визуализации файловой системы, позволяя быстро оценить структуру директорий. Она отображает дерево директорий и файлов, где каждая строка обозначает файл или директорию. Одним из свойств команды является ее возможность ограничивать количество отображаемых файлов с использованием параметра --filelimit
. Однако, если папка превышает указанное ограничение, она просто не будет отображена полностью. В то же время, часто требуется показать часть содержимого директории, даже если общее число файлов превышает лимит.
Пример
Рассмотрим ситуацию: вы хотите просмотреть структуру директорий с ограничением на количество отображаемых файлов в каждой из них. Например, у вас есть корневая директория с несколькими поддиректориями, и в каждой из этих поддиректорий находятся сотни файлов. Используя только команду tree
с параметром --filelimit
, вы бы видели лишь предупреждение о превышении лимита без визуальной информации о структуре большой директории.
В Python предоставляется возможность модифицировать скрипт tree.py
, чтобы обойти это ограничение. С помощью дополнительного параметра -f
, который задает количество отображаемых файлов, можно управлять представлением структуры директорий, ограничивая видимость лишь первых нескольких файлов, включая некоторые ключевые файлы и свертывание оставшихся с указанием количества пропущенных файлов.
Применение
В одной из версий модифицированного скрипта на Python код был адаптирован под задачи вывода структуры с ограничением количества файлов. Например:
def tree(dir, padding, print_files=False, limit=10000):
# Получение списка файлов и директорий
files = sorted([x for x in listdir(dir) if isdir(os.path.join(dir, x)) or print_files])
# Учет лимита
for count, file in enumerate(files, start=1):
path = os.path.join(dir, file)
# Вывод структуры в зависимости от того, файл это или директория
if isdir(path):
print(padding + '+-' + file)
tree(path, padding + '| ', print_files, limit)
else:
if count <= limit:
print(padding + '+-' + file)
elif count == limit + 1:
print(padding + '+- ... <дополнительные файлы>')
break
Таким образом, использовав такой скрипт или его усовершенствованную версию, можно достичь более изящного подхода к отображению структуры файловой системы. Важно заметить, что дополнительная функциональность скрипта позволяет более точно контролировать вывод информации, избегая перегрузки дисплея избыточными данными и упрощая навигацию по сложной файловой системе.
Заключение
Модификация и адаптация инструментов под конкретные нужды – одна из ключевых задач IT-специалиста. В ситуации с tree
, возможно, стандартной функциональности недостаточно, и на помощь приходит программирование с использованием языков вроде Python, что позволяет делать вывод более осмысленным и информативным, фильтруя избыточные данные. Такой подход способствует повышению производительности работы и улучшению опыта взаимодействия с системой.