Как получить список файлов в каталоге в сценарии оболочки?
Если путь пройден без конечного разделителя, к пути будет добавлен разделитель. Для расширения, если введена пустая строка, функция вернет любой файл, имя которого не имеет расширения. Если была введена одна звездочка, будут возвращены все файлы в каталоге. Если длина e больше 0, но не является одиночным *, то перед e будет стоять точка, если e не содержит точку в нулевой позиции.
Для возвращаемого значения. Если возвращается карта нулевой длины, значит, ничего не найдено, но каталог был открыт нормально. Если из возвращаемого значения доступен индекс 999, но размер карты равен 1, это означает, что возникла проблема с открытием пути к каталогу.
Обратите внимание, что для эффективности эту функцию можно разделить на 3 меньшие функции. Кроме того, вы можете создать вызывающую функцию, которая определит, какую функцию она будет вызывать, на основе ввода. Почему это эффективнее? Сказал, что если вы собираетесь захватить все, что является файлом, выполняя этот метод, подфункция, созданная для захвата всех файлов, просто захватит все, что является файлами, и ей не нужно оценивать какие-либо другие ненужные условия каждый раз, когда он находит файл.
Это также применимо к файлам без расширения. Специальная встроенная функция для этой цели будет оценивать погоду только в том случае, если найденный объект является файлом, а затем есть ли в имени файла точка.
Экономия может быть невелика, если вы читаете только каталоги с небольшим количеством файлов. Но если вы читаете большое количество каталогов или если каталог содержит пару сотен тысяч файлов, это может стать огромной экономией.
#include <stdio.h>
#include <sys/stat.h>
#include <iostream>
#include <dirent.h>
#include <map>
std::map<int, std::string> getFile(std::string p, std::string e = "", unsigned char s = '/'){ if ( p.size() > 0 ){ if (p.back() != s) p += s; } if ( e.size() > 0 ){ if ( e.at(0) != '.' && !(e.size() == 1 && e.at(0) == '*') ) e = "." + e; } DIR *dir; struct dirent *ent; struct stat sb; std::map<int, std::string> r = {{999, "FAILED"}}; std::string temp; int f = 0; bool fd; if ( (dir = opendir(p.c_str())) != NULL ){ r.erase (999); while ((ent = readdir (dir)) != NULL){ temp = ent->d_name; fd = temp.find(".") != std::string::npos? true : false; temp = p + temp; if (stat(temp.c_str(), &sb) == 0 && S_ISREG(sb.st_mode)){ if ( e.size() == 1 && e.at(0) == '*' ){ r[f] = temp; f++; } else { if (e.size() == 0){ if ( fd == false ){ r[f] = temp; f++; } continue; } if (e.size() > temp.size()) continue; if ( temp.substr(temp.size() - e.size()) == e ){ r[f] = temp; f++; } } } } closedir(dir); return r; } else { return r; }
}
void printMap(auto &m){ for (const auto &p : m) { std::cout << "m[" << p.first << "] = " << p.second << std::endl; }
}
int main(){ std::map<int, std::string> k = getFile("./", ""); printMap(k); return 0;
} Захват вывода любой команды в индексированный массив bash с элементами, разделенными символом новой строки (\n)
Однако,
$search_dir
содержит множество файлов с пробелами в именах. В этом случае этот сценарий не работает должным образом.
Это решается путем указания bash разделять элементы в строке на основе символа новой строки \n
вместо пробела, который используется по умолчанию IFS
(Внутренний разделитель полей — см. Значение IFS
в сценариях Bash
) переменная, используемая bash. Для этого я рекомендую использовать mapfile
команда.
Инструмент статического анализа кода сценария bash с именем shellscript
рекомендует использовать mapfile
или read -r
всякий раз, когда вы хотите прочитать строку в массив bash, разделяя элементы на основе символа новой строки ( \n
). См.: https://github.com/koalaman/shellcheck/wiki/SC2206
.
(Вернуться к моему первоначальному ответу:)
Вот как преобразовать строку, разделенную новой строкой, в обычный «индексированный» массив bash с помощью mapfile
команда
.
# Capture the output of `ls -1` into a regular bash "indexed" array.
# - includes both files AND directories!
mapfile -t allfilenames_array <<< "$(ls -1)"
# Capture the output of `find` into a regular bash "indexed" array
# - includes directories ONLY!
# Note: for other `-type` options, see `man find`.
mapfile -t dirnames_array \ <<< "$(find . -mindepth 1 -maxdepth 1 -type d | sort -V)" - Мы используем
ls -1
(это «дефисное число_один»), чтобы каждое имя файла помещалось в отдельную строку, тем самым разделяя их все символом новой строки\n
чар. - Если хотите погуглить,
<<<
в bash называется «здесь строка». - См.
mapfile --help
, илиhelp mapfile
, для помощи.
Пример полного кода:
Из файла array_list_all_files_and_directories.sh
в моем eRCaGuy_hello_world
репо:
echo "Output of 'ls -1'"
echo "-----------------"
ls -1
echo ""
# Capture the output of `ls -1` into a regular bash "indexed" array.
# - includes both files AND directories!
mapfile -t allfilenames_array <<< "$(ls -1)"
# Capture the output of `find` into a regular bash "indexed" array
# - includes directories ONLY!
# Note: for other `-type` options, see `man find` and see my answer here:
# https://stackoverflow.com/a/71345102/4561887
mapfile -t dirnames_array \ <<< "$(find . -mindepth 1 -maxdepth 1 -type d | sort -V)"
# Get the number of elements in each array
allfilenames_array_len="${#allfilenames_array[@]}"
dirnames_array_len="${#dirnames_array[@]}"
# 1. Now manually print all elements in each array
echo "All filenames (files AND dirs) (count = $allfilenames_array_len):"
for filename in "${allfilenames_array[@]}"; do echo " $filename"
done
echo "Dirnames ONLY (count = $dirnames_array_len):"
for dirname in "${dirnames_array[@]}"; do # remove the `./` from the beginning of each dirname dirname="$(basename "$dirname")" echo " $dirname"
done
echo ""
# OR, 2. manually print the index number followed by all elements in the array
echo "All filenames (files AND dirs) (count = $allfilenames_array_len):"
for i in "${!allfilenames_array[@]}"; do printf " %3i: %s\n" "$i" "${allfilenames_array["$i"]}"
done
echo "Dirnames ONLY (count = $dirnames_array_len):"
for i in "${!dirnames_array[@]}"; do # remove the `./` from the beginning of each dirname dirname="$(basename "${dirnames_array["$i"]}")" printf " %3i: %s\n" "$i" "$dirname"
done
echo "" eRCaGuy_hello_world/python$ ../bash/array_list_all_files_and_directories.sh
Output of 'ls -1'
-----------------
autogenerate_c_or_cpp_code.py
autogenerated
auto_white_balance_img.py
enum_practice.py
raw_bytes_practice.py
slots_practice
socket_talk_to_ethernet_device.py
textwrap_practice_1.py
yaml_import
All filenames (files AND dirs) (count = 9): autogenerate_c_or_cpp_code.py autogenerated auto_white_balance_img.py enum_practice.py raw_bytes_practice.py slots_practice socket_talk_to_ethernet_device.py textwrap_practice_1.py yaml_import
Dirnames ONLY (count = 3): autogenerated slots_practice yaml_import
All filenames (files AND dirs) (count = 9): 0: autogenerate_c_or_cpp_code.py 1: autogenerated 2: auto_white_balance_img.py 3: enum_practice.py 4: raw_bytes_practice.py 5: slots_practice 6: socket_talk_to_ethernet_device.py 7: textwrap_practice_1.py 8: yaml_import
Dirnames ONLY (count = 3): 0: autogenerated 1: slots_practice 2: yaml_import Предварительные заметки
Хотя есть четкое различие между файлом
и справочник
термины в тексте вопроса, некоторые могут возразить, что каталоги на самом деле являются специальными файламиУтверждение: “ все файлы каталога
“можно интерпретировать двояко:Все прямые
(или уровень 1) только потомкиВсе потомки во всем дереве каталогов (включая потомков в подкаталогах)
Когда был задан вопрос, я представил, что Python 2
, был LTS
версия, однако примеры кода будут выполняться Python 3
( 0,5
)
(Я оставлю их как Python 2
максимально совместимый; также любой код, принадлежащий Python
то, что я собираюсь опубликовать, взято из v3.5.4
– если не указано иное).That has consequences related to another keyword in the question: ” add them into a list
“:In Python 3
, generator is the default behavior
>>> import sys >>> >>> sys.version '2.7.10 (default, Mar 8 2016, 15:02:46) [MSC v.1600 64 bit (AMD64)]' >>> m = map(lambda x: x, [1, 2, 3]) # Just a dummy lambda function >>> m, type(m) ([1, 2, 3], <type 'list'>) >>> len(m) 3>>> import sys >>> >>> sys.version '3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)]' >>> m = map(lambda x: x, [1, 2, 3]) >>> m, type(m) (<map object at 0x000001B4257342B0>, <class 'map'>) >>> len(m) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: object of type 'map' has no len() >>> lm0 = list(m) # Build a list from the generator >>> lm0, type(lm0) ([1, 2, 3], <class 'list'>) >>> >>> lm1 = list(m) # Build a list from the same generator >>> lm1, type(lm1) # Empty list now - generator already exhausted ([], <class 'list'>)[cfati@CFATI-5510-0:e:\Work\Dev\StackOverflow\q003207219]> sopr.bat ### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ### [prompt]> [prompt]> tree /f "root_dir" Folder PATH listing for volume Work Volume serial number is 00000029 3655:6FED E:\WORK\DEV\STACKOVERFLOW\Q003207219\ROOT_DIR ¦ file0 ¦ file1 ¦ +---dir0 ¦ +---dir00 ¦ ¦ ¦ file000 ¦ ¦ ¦ ¦ ¦ +---dir000 ¦ ¦ file0000 ¦ ¦ ¦ +---dir01 ¦ ¦ file010 ¦ ¦ file011 ¦ ¦ ¦ +---dir02 ¦ +---dir020 ¦ +---dir0200 +---dir1 ¦ file10 ¦ file11 ¦ file12 ¦ +---dir2 ¦ ¦ file20 ¦ ¦ ¦ +---dir20 ¦ file200 ¦ +---dir3
Работа с каталогами
Последнее обновление: 07.01.2022
Для работы с каталогами в пространстве имен System. IO предназначены сразу два класса: и
.
Класс Directory
Статический класс Directory предоставляет ряд методов для управления каталогами. Некоторые из этих методов:
: создает каталог по указанному пути path
: удаляет каталог по указанному пути path
: определяет, существует ли каталог по указанному пути path. Если существует, возвращается
true
,
если не существует, тоfalse: получает путь к текущей папке
: получает список подкаталогов в каталоге path
: получает список файлов в каталоге path
: получает список подкаталогов и файлов в каталоге path
: перемещает каталог
: получение родительского каталога
: возвращает время последнего изменения каталога
: возвращает время последнего обращения к каталогу
: возвращает время создания каталога
Класс DirectoryInfo
Данный класс предоставляет функциональность для создания, удаления, перемещения и других операций с каталогами. Во многом он похож на
Directory, но не является статическим.
Для создания объекта класса DirectoryInfo применяется конструктор, который в качестве параметра принимает путь к каталогу:
public DirectoryInfo (string path);
Основные методы класса :
: создает каталог
: создает подкаталог по указанному пути path
: удаляет каталог
: получает список подкаталогов папки в виде массива DirectoryInfo
: получает список файлов в папке в виде массива FileInfo
: перемещает каталог
Основные свойства класса :
: представляет время создания каталога
: представляет время последнего доступа к каталогу
: представляет время последнего изменения каталога
: определяет, существует ли каталог
: получение родительского каталога
: получение корневого каталога
: имя каталога
: полный путь к каталогу
Directory или DirectoryInfo
Как видно из функционала, оба класса предоставляют похожие возможности. Когда же и что использовать? Если надо совершить одну-две операции с одним каталогом, то проще
использовать класс Directory. Если необходимо выполнить последовательность операций с одним и тем же каталогом, то лучше воспользоваться классом
DirectoryInfo. Почему? Дело в том, что методы класса Directory выполняют дополнительные проверки безопасности. А для класса DirectoryInfo
такие проверки не всегда обязательны.
Посмотрим на примерах применение этих классов
Получение списка файлов и подкаталогов
string dirName = "C:\\";
// если папка существует
if (Directory. Exists(dirName))
{ Console. WriteLine("Подкаталоги:"); string[] dirs = Directory. GetDirectories(dirName); foreach (string s in dirs) { Console. WriteLine(s); } Console. WriteLine(); Console. WriteLine("Файлы:"); string[] files = Directory. GetFiles(dirName); foreach (string s in files) { Console. WriteLine(s); }
} Аналогичный пример с DirectoryInfo:
string dirName = "C:\";
var directory = new DirectoryInfo(dirName);
if (directory. Exists)
{ Console. WriteLine("Подкаталоги:"); DirectoryInfo[] dirs = directory. GetDirectories(); foreach (DirectoryInfo dir in dirs) { Console. WriteLine(dir. FullName); } Console. WriteLine(); Console. WriteLine("Файлы:"); FileInfo[] files = directory. GetFiles(); foreach (FileInfo file in files) { Console. WriteLine(file. FullName); }
} Фильтрация папок и файлов
Методы получения папок и файлов позволяют выполнять фильтрацию. В качестве фильтра в эти методы передается шаблон, который может содержать два
плейсхолдера: или символ-звездочка (соответствует любому количеству символов) и или вопросительный знак (соответствует одному символу)
Например, найдем все папки, которые начинаются на “books”:
// класс Directory
string[] dirs = Directory. GetDirectories(dirName, "books*.");
// класс DirectoryInfo
var directory = new DirectoryInfo(dirName);
DirectoryInfo[] dirs = directory. GetDirectories("books*."); Или получим все файлы с расширением “.exe”:
// класс Directory
string[] files = Directory. GetFiles(dirName, "*.exe");
// класс DirectoryInfo
var directory = new DirectoryInfo(dirName);
FileInfo[] files = directory. GetFiles("*.exe"); Создание каталога
string path = @"C:\SomeDir";
string subpath = @"program\avalon";
DirectoryInfo dirInfo = new DirectoryInfo(path);
if (!dirInfo. Exists)
{ dirInfo. Create();
}
dirInfo. CreateSubdirectory(subpath); Вначале проверяем, а нету ли такой директории, так как если она существует, то ее создать будет нельзя, и приложение выбросит ошибку.
В итоге у нас получится следующий путь: “C:\SomeDir\program\avalon”
Аналогичный пример с классом Directory:
string path = @"C:\SomeDir";
string subpath = @"program\avalon";
if (!Directory. Exists(path))
{ Directory. CreateDirectory(path);
}
Directory. CreateDirectory($"{path}/{subpath}"); Получение информации о каталоге
string dirName = "C:\\Program Files";
DirectoryInfo dirInfo = new DirectoryInfo(dirName);
Console. WriteLine($"Название каталога: {dirInfo. Name}");
Console. WriteLine($"Полное название каталога: {dirInfo. FullName}");
Console. WriteLine($"Время создания каталога: {dirInfo. CreationTime}");
Console. WriteLine($"Корневой каталог: {dirInfo. Root}"); Удаление каталога
Если мы просто применим метод Delete
к непустой папке, в которой есть какие-нибудь файлы или подкаталоги, то приложение
нам выбросит ошибку. Поэтому нам надо передать в метод Delete
дополнительный параметр булевого типа, который укажет, что папку
надо удалять со всем содержимым. Кроме того, перед удалением следует проверить наличие удаляемой папки, иначе приложение выбросит исключение:
string dirName = @"C:\SomeDir";
DirectoryInfo dirInfo = new DirectoryInfo(dirName);
if (dirInfo. Exists)
{ dirInfo. Delete(true); Console. WriteLine("Каталог удален");
}
else
{ Console. WriteLine("Каталог не существует");
} string dirName = @"C:\SomeDir";
if (Directory. Exists(dirName))
{ Directory. Delete(dirName, true); Console. WriteLine("Каталог удален");
}
else
{ Console. WriteLine("Каталог не существует");
} Перемещение каталога
При перемещении надо учитывать, что новый каталог, в который мы хотим перемесить все содержимое старого каталога, не должен существовать.
string oldPath = @"C:\SomeFolder";
string newPath = @"C:\SomeDir";
DirectoryInfo dirInfo = new DirectoryInfo(oldPath);
if (dirInfo. Exists && !Directory. Exists(newPath))
{ dirInfo. MoveTo(newPath); // или так // Directory. Move(oldPath, newPath);
} Перемещение каталога в рамках одной папки (как в примере выше) фактически аналогично переименованию папки
List different types of files using find and ls
- For
find
, see this answer
. See also my comment here
. - Для
ls
, см. linuxhandbook.com: Как составить список только каталогов в Linux
Совет:
для любого из find
примеры ниже, вы можете направить вывод на sort -V
если вы хотите, чтобы это отсортировано.
find . -maxdepth 1 -type f | sort -V Список только обычных файлов
(-type f
) 1 уровень в глубину:
# General form
find "path/to/some/dir" -maxdepth 1 -type f
# In current directory
find . -maxdepth 1 -type f Только список символические ссылки
( -type l
) 1 уровень в глубину:
# General form
find "path/to/some/dir" -maxdepth 1 -type l
# In current directory
find . -maxdepth 1 -type l Список только справочников
( -type d
) 1 уровень в глубину:
Обратите внимание, что для find
например здесь мы также добавляем -mindepth 1
чтобы исключить текущий каталог, .
, что будет напечатано как .
В противном случае вверху списка каталогов. См. здесь: Как исключить эту папку /current/dot из поиска “типа d”
# General form
find "path/to/some/dir" -mindepth 1 -maxdepth 1 -type d
# In current directory
find . -mindepth 1 -maxdepth 1 -type d
# OR, using `ls`:
ls -d Объединить некоторые из вышеперечисленных: перечислить только обычные файлы
и символические ссылки
( -type f,l
) 1 уровень в глубину:
Используйте запятую ( ,
) для разделения аргументов на -type
:
# General form
find "path/to/some/dir" -maxdepth 1 -type f,l
# In current directory
find . -maxdepth 1 -type f,l Решения
Программные подходы
1. [Питон. Документы]: os.listdir(path=’.’)
>>> import os >>> >>> root_dir = "root_dir" # Path relative to current dir (os.getcwd()) >>> >>> os.listdir(root_dir) # List all the items in root_dir ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1'] >>> >>> [item for item in os.listdir(root_dir) if os.path.isfile(os.path.join(root_dir, item))] # Filter items and only keep files (strip out directories) ['file0', 'file1']
Более сложный пример ( code_os_listdir.py
):
#!/usr/bin/env python
import os
import sys
from pprint import pformat as pf
def _get_dir_content(path, include_folders, recursive): entries = os.listdir(path) for entry in entries: entry_with_path = os.path.join(path, entry) if os.path.isdir(entry_with_path): if include_folders: yield entry_with_path if recursive: for sub_entry in _get_dir_content(entry_with_path, include_folders, recursive): yield sub_entry else: yield entry_with_path
def get_dir_content(path, include_folders=True, recursive=True, prepend_folder_name=True): path_len = len(path) + len(os.path.sep) for item in _get_dir_content(path, include_folders, recursive): yield item if prepend_folder_name else item[path_len:]
def _get_dir_content_old(path, include_folders, recursive): entries = os.listdir(path) ret = list() for entry in entries: entry_with_path = os.path.join(path, entry) if os.path.isdir(entry_with_path): if include_folders: ret.append(entry_with_path) if recursive: ret.extend(_get_dir_content_old(entry_with_path, include_folders, recursive)) else: ret.append(entry_with_path) return ret
def get_dir_content_old(path, include_folders=True, recursive=True, prepend_folder_name=True): path_len = len(path) + len(os.path.sep) return [item if prepend_folder_name else item[path_len:] for item in _get_dir_content_old(path, include_folders, recursive)]
def main(*argv): root_dir = "root_dir" ret0 = get_dir_content(root_dir, include_folders=True, recursive=True, prepend_folder_name=True) lret0 = list(ret0) print("{:} {:d}\n{:s}".format(ret0, len(lret0), pf(lret0))) ret1 = get_dir_content_old(root_dir, include_folders=False, recursive=True, prepend_folder_name=False) print("\n{:d}\n{:s}".format(len(ret1), pf(ret1)))
if __name__ == "__main__": print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform)) rc = main(*sys.argv[1:]) print("\nDone.\n") sys.exit(rc) Есть две реализации:
Тот, который использует генераторы (конечно здесь он кажется бесполезным, так как я сразу конвертирую результат в список)
Классический (имена функций заканчиваются на _old
)
Используется рекурсия (для попадания в подкаталоги)
Для каждой реализации есть две функции:
Тот, который начинается со знака подчеркивания
( _
): “private” (не следует вызывать напрямую) – это делает всю работуОбщедоступный (обертка над предыдущим): он просто удаляет начальный путь (если требуется) из возвращаемых записей. Это уродливая реализация, но это единственная идея, которая пришла мне в голову на данный момент
С точки зрения производительности генераторы, как правило, немного быстрее (учитывая как , так и создание
и итерация
раз), но я не тестировал их в рекурсивных функциях, а также я выполняю итерацию внутри функции по внутренним генераторам – не знаю, насколько это удобно для производительностиИграйте с аргументами, чтобы получить разные результаты
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.05.04_test0\Scripts\python.exe" ".\code_os_listdir.py" Python 3.5.4 (v3.5.4:3f56838, Aug 8 2017, 02:17:05) [MSC v.1900 64 bit (AMD64)] 064bit on win32 <generator object get_dir_content at 0x000002C080418F68> 22 ['root_dir\\dir0', 'root_dir\\dir0\\dir00', 'root_dir\\dir0\\dir00\\dir000', 'root_dir\\dir0\\dir00\\dir000\\file0000', 'root_dir\\dir0\\dir00\\file000', 'root_dir\\dir0\\dir01', 'root_dir\\dir0\\dir01\\file010', 'root_dir\\dir0\\dir01\\file011', 'root_dir\\dir0\\dir02', 'root_dir\\dir0\\dir02\\dir020', 'root_dir\\dir0\\dir02\\dir020\\dir0200', 'root_dir\\dir1', 'root_dir\\dir1\\file10', 'root_dir\\dir1\\file11', 'root_dir\\dir1\\file12', 'root_dir\\dir2', 'root_dir\\dir2\\dir20', 'root_dir\\dir2\\dir20\\file200', 'root_dir\\dir2\\file20', 'root_dir\\dir3', 'root_dir\\file0', 'root_dir\\file1'] 11 ['dir0\\dir00\\dir000\\file0000', 'dir0\\dir00\\file000', 'dir0\\dir01\\file010', 'dir0\\dir01\\file011', 'dir1\\file10', 'dir1\\file11', 'dir1\\file12', 'dir2\\dir20\\file200', 'dir2\\file20', 'file0', 'file1'] Done.
2. [Питон. Документы]: os.scandir(path=’.’)
Возвращает итератор os. ДирЭнтри
объекты, соответствующие записям в каталоге, заданном путем
. Записи выдаются в произвольном порядке, а специальные записи'.'
и'..'
не включены.Использование scandir()
вместо listdir()
может значительно повысить производительность кода, которому также требуется информация о типе файла или атрибуте файла, поскольку os. ДирЭнтри
объекты раскрывают эту информацию, если операционная система предоставляет ее при сканировании каталога. Все ос. ДирЭнтри
методы могут выполнять системный вызов, но is_dir()
и is_file()
обычно требуется только системный вызов для символических ссылок; ок. DirEntry.stat()
всегда требует системного вызова в Unix, но требует его только для символических ссылок в Windows.
>>> import os >>> >>> root_dir = os.path.join(".", "root_dir") # Explicitly prepending current directory >>> root_dir '.\\root_dir' >>> >>> scandir_iterator = os.scandir(root_dir) >>> scandir_iterator <nt.ScandirIterator object at 0x00000268CF4BC140> >>> [item.path for item in scandir_iterator] ['.\\root_dir\\dir0', '.\\root_dir\\dir1', '.\\root_dir\\dir2', '.\\root_dir\\dir3', '.\\root_dir\\file0', '.\\root_dir\\file1'] >>> >>> [item.path for item in scandir_iterator] # Will yield an empty list as it was consumed by previous iteration (automatically performed by the list comprehension) [] >>> >>> scandir_iterator = os.scandir(root_dir) # Reinitialize the generator >>> for item in scandir_iterator : ... if os.path.isfile(item.path): ... print(item.name) ... file0 file1
Аналогично os.listdir
Но он также более гибкий (и предлагает больше функций), больше Python
ic (а в некоторых случаях и быстрее)
3. [Питон. Документы]: os.walk(top, topdown=True, onerror=None, followlinks=False)
Создание имен файлов в дереве каталогов путем обхода дерева сверху вниз или снизу вверх. Для каждого каталога в дереве с корнем в каталоге вверху
(в том числе топ
сам), он дает 3-кортеж (dirpath
,dirnames
,filenames
).
>>> import os >>> >>> root_dir = os.path.join(os.getcwd(), "root_dir") # Specify the full path >>> root_dir 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir' >>> >>> walk_generator = os.walk(root_dir) >>> root_dir_entry = next(walk_generator) # First entry corresponds to the root dir (passed as an argument) >>> root_dir_entry ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir', ['dir0', 'dir1', 'dir2', 'dir3'], ['file0', 'file1']) >>> >>> root_dir_entry[1] + root_dir_entry[2] # Display dirs and files (direct descendants) in a single list ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1'] >>> >>> [os.path.join(root_dir_entry[0], item) for item in root_dir_entry[1] + root_dir_entry[2]] # Display all the entries in the previous list by their full path ['E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir1', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir3', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\file0', 'E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\file1'] >>> >>> for entry in walk_generator: # Display the rest of the elements (corresponding to every subdir) ... print(entry) ... ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0', ['dir00', 'dir01', 'dir02'], []) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir00', ['dir000'], ['file000']) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir00\\dir000', [], ['file0000']) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir01', [], ['file010', 'file011']) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02', ['dir020'], []) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02\\dir020', ['dir0200'], []) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir0\\dir02\\dir020\\dir0200', [], []) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir1', [], ['file10', 'file11', 'file12']) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2', ['dir20'], ['file20']) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir2\\dir20', [], ['file200']) ('E:\\Work\\Dev\\StackOverflow\\q003207219\\root_dir\\dir3', [], [])
Под сценами используется os.scandir
( os.listdir
на более старых ( Python
) версии)Он делает тяжелую работу, повторяясь во вложенных папках
4. [Питон. Документы]: glob.glob(pathname, *, root_dir=None, dir_fd=None, recursive=False, include_hidden=False)
>>> import glob, os >>> >>> wildcard_pattern = "*" >>> root_dir = os.path.join("root_dir", wildcard_pattern) # Match every file/dir name >>> root_dir 'root_dir\\*' >>> >>> glob_list = glob.glob(root_dir) >>> glob_list ['root_dir\\dir0', 'root_dir\\dir1', 'root_dir\\dir2', 'root_dir\\dir3', 'root_dir\\file0', 'root_dir\\file1'] >>> >>> [item.replace("root_dir" + os.path.sep, "") for item in glob_list] # Strip the dir name and the path separator from begining ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1'] >>> >>> for entry in glob.iglob(root_dir + "*", recursive=True): ... print(entry) ... root_dir\ root_dir\dir0 root_dir\dir0\dir00 root_dir\dir0\dir00\dir000 root_dir\dir0\dir00\dir000\file0000 root_dir\dir0\dir00\file000 root_dir\dir0\dir01 root_dir\dir0\dir01\file010 root_dir\dir0\dir01\file011 root_dir\dir0\dir02 root_dir\dir0\dir02\dir020 root_dir\dir0\dir02\dir020\dir0200 root_dir\dir1 root_dir\dir1\file10 root_dir\dir1\file11 root_dir\dir1\file12 root_dir\dir2 root_dir\dir2\dir20 root_dir\dir2\dir20\file200 root_dir\dir2\file20 root_dir\dir3 root_dir\file0 root_dir\file1
Для больших деревьев (особенно если рекурсивный
горит), иглоб
предпочтительнееРазрешает расширенную фильтрацию на основе имени (из-за подстановочного знака)
5. [Питон. Документы]: class pathlib. Путь (* сегменты пути)
>>> import pathlib >>> >>> root_dir = "root_dir" >>> root_dir_instance = pathlib.Path(root_dir) >>> root_dir_instance WindowsPath('root_dir') >>> root_dir_instance.name 'root_dir' >>> root_dir_instance.is_dir() True >>> >>> [item.name for item in root_dir_instance.glob("*")] # Wildcard searching for all direct descendants ['dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1'] >>> >>> [os.path.join(item.parent.name, item.name) for item in root_dir_instance.glob("*") if not item.is_dir()] # Display paths (including parent) for files only ['root_dir\\file0', 'root_dir\\file1']
Это один
способ достижения нашей целиЭто ООП
стиль обработки путейПредлагает множество функций
6. [Python 2. Документы]: dircache.listdir(путь)
Питон 2
только
def listdir(path): """List directory contents, using cache.""" try: cached_mtime, list = cache[path] del cache[path] except KeyError: cached_mtime, list = -1, [] mtime = os.stat(path).st_mtime if mtime != cached_mtime: list = os.listdir(path) list.sort() cache[path] = mtime, list return list
7. Родная ОС
API
с
#!/usr/bin/env python3
import ctypes as cts
import sys
DT_DIR = 4
DT_REG = 8
class NixDirent64(cts.Structure): _fields_ = ( ("d_ino", cts.c_ulonglong), ("d_off", cts.c_longlong), ("d_reclen", cts.c_ushort), ("d_type", cts.c_ubyte), ("d_name", cts.c_char * 256), )
NixDirent64Ptr = cts.POINTER(NixDirent64)
libc = this_process = cts.CDLL(None, use_errno=True)
opendir = libc.opendir
opendir.argtypes = (cts.c_char_p,)
opendir.restype = cts.c_void_p
readdir = libc.readdir
readdir.argtypes = (cts.c_void_p,)
readdir.restype = NixDirent64Ptr
closedir = libc.closedir
closedir.argtypes = (cts.c_void_p,)
def get_dir_content(path): ret = [path, [], []] pdir = opendir(cts.create_string_buffer(path.encode())) if not pdir: print("opendir returned NULL (errno: {:d})".format(cts.get_errno())) return ret cts.set_errno(0) while True: pdirent = readdir(pdir) if not pdirent: break dirent = pdirent.contents name = dirent.d_name.decode() if dirent.d_type & DT_DIR: if name not in (".", ".."): ret[1].append(name) elif dirent.d_type & DT_REG: ret[2].append(name) if cts.get_errno(): print("readdir returned NULL (errno: {:d})".format(cts.get_errno())) closedir(pdir) return ret
def main(*argv): root_dir = "root_dir" entries = get_dir_content(root_dir) print("Entries:\n{:}".format(entries))
if __name__ == "__main__": print("Python {:s} {:03d}bit on {:s}\n".format(" ".join(elem.strip() for elem in sys.version.split("\n")), 64 if sys.maxsize > 0x100000000 else 32, sys.platform)) rc = main(*sys.argv[1:]) print("\nDone.\n") sys.exit(rc) Возвращает данные в os.walk
формат. Я не удосужился сделать его рекурсивным, но, начиная с существующего кода, это было бы довольно тривиальной задачей
[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q003207219]> python3.5 ./code_ctypes.py Python 3.5.10 (default, Jan 15 2022, 19:53:00) [GCC 9.3.0] 064bit on linux Entries: ['root_dir', ['dir0', 'dir1', 'dir2', 'dir3'], ['file0', 'file1']] Done.
8. [TimGolden]: win32file. FindFilesW
Получает список совпадающих имен файлов, используя Windows Unicode API. Интерфейс к функциям API FindFirstFileW/FindNextFileW/Find close.
>>> import os, win32file as wfile, win32con as wcon >>> >>> root_dir = "root_dir" >>> root_dir_wildcard = os.path.join(root_dir, "*") >>> entry_list = wfile.FindFilesW(root_dir_wildcard) >>> len(entry_list) # Don't display the whole content as it's too long 8 >>> [entry[-2] for entry in entry_list] # Only display the entry names ['.', '..', 'dir0', 'dir1', 'dir2', 'dir3', 'file0', 'file1'] >>> >>> [entry[-2] for entry in entry_list if entry[0] & wcon.FILE_ATTRIBUTE_DIRECTORY and entry[-2] not in (".", "..")] # Filter entries and only display dir names (except self and parent) ['dir0', 'dir1', 'dir2', 'dir3'] >>> >>> [os.path.join(root_dir, entry[-2]) for entry in entry_list if entry[0] & (wcon.FILE_ATTRIBUTE_NORMAL | wcon.FILE_ATTRIBUTE_ARCHIVE)] # Only display file "full" names ['root_dir\\file0', 'root_dir\\file1']
- win32file. FindFilesW
является частью [GitHub]: mhammond/pywin32 — расширения Python для Windows (pywin32)
, который представляет собой Python
обертка над WinAPI
с
9. Использовать некоторые (другие) 3 рд
пакет для вечеринки, который делает свое дело
Скорее всего, будет опираться на один (или несколько) из вышеперечисленных (возможно, с небольшими изменениями).
Примечания:
Код должен быть переносимым (за исключением мест, предназначенных для определенной области, которые помечены) или пересекаться:
ОС
( Никс
, Победа
, )Питон
версия (2, 3, )
В приведенных выше вариантах использовалось несколько стилей пути (абсолютный, относительный), чтобы проиллюстрировать тот факт, что используемые «инструменты» гибки в этом направлении
_get_dir_content
(из точки №1.
) может быть реализован с использованием любого из этих подходов (некоторые потребуют больше работы, а некоторые меньше)- Некоторая расширенная фильтрация (вместо простого файла против
реж.
) можно сделать: например.
include_folders
аргумент может быть заменен другим (например, filter_func
), которая будет функцией, принимающей путь в качестве аргумента:filter_func=lambda x: True
(это ничего не удаляет) и внутри _get_dir_content
что-то вроде:if not filter_func(entry_with_path): continue
(если функция не работает для одной записи, она будет пропущена), но чем сложнее становится код, тем дольше он будет выполняться
- Некоторая расширенная фильтрация (вместо простого файла против
Другие подходы:
1. Используйте Python
только в обертке
Все делается по другой технологии
Эта технология вызвана из Python
Использование Python
(или любой язык программирования в этом отношении) для выполнения Shell
команды (и анализировать их выходные данные)Некоторые считают это ловким хаком
Я считаю, что это скорее хромой обходной путь ( gainarie
), как действие как таковое
выполняется из Раковины
( Команд
в данном случае) и, таким образом, не имеет ничего общего с Python
[prompt]> "e:\Work\Dev\VEnvs\py_pc064_03.05.04_test0\Scripts\python.exe" -c "import os;os.system(\"dir /b root_dir\")" dir0 dir1 dir2 dir3 file0 file1
[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackOverflow/q003207219]> python3.5 -c "import os;os.system(\"ls root_dir\")" dir0 dir1 dir2 dir3 file0 file1
Как правило, этого подхода следует избегать, поскольку, если какой-либо формат вывода команды немного отличается между ОС
версий/разновидностей, код парсинга также должен быть адаптирован, не говоря уже о различиях между локалями.



