Введите путь к файлу и проанализируйте его, выводя каждую подпапку на новой строке

Обновлено: 21.11.2024

Возвращает массив файлов и каталогов из каталога .

Параметры

Каталог, который будет просканирован.

По умолчанию сортировка осуществляется в алфавитном порядке по возрастанию. Если необязательный sorting_order установлен в SCANDIR_SORT_DESCENDING , то порядок сортировки будет алфавитным в порядке убывания. Если установлено значение SCANDIR_SORT_NONE, результат не сортируется.

Описание параметра контекста см. в разделе руководства о потоках.

Возвращаемые значения

Возвращает массив имен файлов в случае успеха или false в случае ошибки. Если каталог не является каталогом, то возвращается логическое значение false и генерируется ошибка уровня E_WARNING.

Журнал изменений

Версия Описание
8.0.0 контекст теперь можно обнулить.

Примеры

= '/tmp';
$files1 = скандир ( $каталог );
$files2 = скандир ($дир, 1);

print_r ( $files1 );
print_r ( $files2 );
?>

Приведенный выше пример выведет что-то похожее на:

Примечания

URL-адрес можно использовать в качестве имени файла с этой функцией, если включена оболочка fopen. См. fopen() для более подробной информации о том, как указать имя файла. См. Поддерживаемые протоколы и оболочки для получения ссылок на информацию о возможностях различных оболочек, заметок об их использовании и информации о любых предопределенных переменных, которые они могут предоставлять.

См. также

  • opendir() — открыть дескриптор каталога
  • readdir() — чтение записи из дескриптора каталога
  • glob() – находит пути, соответствующие шаблону.
  • is_dir() — сообщает, является ли имя файла каталогом
  • sort() — сортировка массива по возрастанию

Пользовательские заметки 37 заметок

Простой способ избавиться от точек, которые scandir() обнаруживает в среде Linux:

= '/path/to/my/directory' ;
$scanned_directory = array_diff ( scandir ( $directory ), array( '..' , '.' ));
?>

Вот мои 2 цента. Я хотел рекурсивно создать массив моей структуры каталогов. Я хотел легко получить доступ к данным в определенном каталоге, используя foreach. У меня получилось следующее:

функция dirToArray ( $dir )

$cdir = скандир ( $dir );
foreach ( $cdir as $key => $value )
<
if (! in_array ( $value ,array( "." , ".." )))
<
if ( is_dir ( $dir . DIRECTORY_SEPARATOR . $value ))
<
$result [ $value ] = dirToArray ( $dir . DIRECTORY_SEPARATOR . $value );
>
еще
<
$result [] = $value ;
>
>
>

вернуть $результат;
>
?>

Вывод
Массив
(
[subdir1] => Массив
(
[ 0] => file1.txt
[subsubdir] => Array
(
[0] => file2.txt
[1] => file3.txt
)
)
[subdir2] => Массив
(
[0] => file4.txt
>
)

Кто-то написал, что array_slice можно использовать для быстрого удаления записей каталога "." а также "..". Однако «-» — это допустимая запись, которая должна стоять перед ними, поэтому array_slice удалит неправильные записи.

Как я решил проблему с '.' и '..'

$x = scandir__DIR__; //любой каталог
foreach ($x as $key => $value) if ('.' !== $value && '..' !== $value) echo $value;
>
>
Просто и работает

Самый быстрый способ получить список файлов без точек.
= array_slice ( scandir ( '/path/to/directory/' ), 2 );

Для каталога, содержащего такие файлы, как (например) -.jpg, результаты scandir немного "странные" ;)

= '/somedir' ;
$files = scandir ( $dir );
print_r ( $files );
?>

Массив
(
[0] => -.jpg
[1] => .
[2] = > ..
[3] => foo.txt
[4] => somedir
)

Осторожно, сортировка осуществляется в порядке ASCII :)

Мне нужно было найти способ получить полный путь ко всем файлам в каталоге и всех подкаталогах каталога.
Вот мое решение: рекурсивные функции!

функция find_all_files ( $dir )
<
$root = scandir ( $dir );
foreach( $root as $value )
<
if( $value === '.' || $value === '..' )
if( is_file ( "$dir / $value") < $result []= "$dir / $value" ;continue;>
foreach( find_all_files ("$dir / $value" ) as $value )
<
$result []= $value ;
>
>
возврат $result ;
>
?>

Нужно было что-то, что могло бы возвращать содержимое одного или нескольких каталогов, рекурсивно или нерекурсивно,
для всех файлов или указанных расширений файлов, которые были бы
легко доступны из любой области или скрипта.

И я хотел разрешить перегрузку, потому что иногда мне лень передавать все параметры.
класс scanDir static private $directories , $files , $ext_filter , $recursive ;

// ------------------------------------------------------------ -------------------------------------------------- < br />// scan(dirpath::string|array, extensions::string|array, recursive::true|false)
static public function scan() // Инициализировать значения по умолчанию
self :: $ рекурсивный = ложь;
self :: $directories = array();
self :: $files = array();
я :: $ext_filter = false ;

// Проверяем наличие минимальных параметров
if(! $args = func_get_args ()) die("Необходимо указать строку пути или массив строк пути" );
>
if( gettype ( $args [ 0 ]) != "string" && gettype ( $args [ 0 ]) != "array" ) die( "Должен предоставить строку пути или массив строки пути");
>

// Проверяем, рекурсивное сканирование | действие по умолчанию: нет подкаталогов
if(isset( $args [ 2 ]) && $args [ 2 ] == true )

// Был ли включен фильтр по расширениям файлов? | действие по умолчанию: вернуть все типы файлов
if(isset($args[1])) if(gettype($args[1]) == "array")< self :: $ext_filter = array_map('strtolower' , $args [1]);>
else
if( gettype ( $args [ 1 ]) == "string" )< self :: $ext_filter [] = strtolower ( $args [ 1 ] );>
>

// Захват пути(ов)
self :: verifyPaths ( $args [ 0 ]);
вернуть себя :: $files ;
>

статическая приватная функция verifyPaths ( $paths ) $path_errors = array();
if(gettype ( $paths ) == "строка")

foreach($paths as $path) if(is_dir($path)) self :: $directories[] = $path;
$dirContents = self :: find_contents ( $path );
> else $path_errors [] = $path ;
>
>

// Вот как мы сканируем каталоги
static private function find_contents ( $dir ) $result = array();
$root = scandir ( $dir );
foreach( $root as $value ) if( $value === '.' || $value === '..' )
if( is_file ( $dir . DIRECTORY_SEPARATOR . $value ) ) if(! self :: $ext_filter || in_array ( strtolower ( pathinfo ( $dir . DIRECTORY_SEPARATOR . $value , PATHINFO_EXTENSION )), self :: $ext_filter )) self :: $files [] = $result [] = $ реж. DIRECTORY_SEPARATOR . $значение;
>
продолжить;
>
if( self :: $recursive ) foreach( self :: find_contents ( $dir . DIRECTORY_SEPARATOR . $value ) as $value ) self :: $files [] = $result [] = $ стоимость ;
>
>
>
// Возврат требуется для рекурсивного поиска
return $result ;
>
>
?>

Использование:
scanDir::scan(path(s):string|массив, [file_extensions:string|массив ], [подпапки?:true|false]);
//Сканировать один каталог на наличие всех файлов, без подкаталогов
$files = scanDir :: scan ( 'D:\Websites\temp' );

//Сканировать несколько каталогов на наличие всех файлов, без подкаталогов
$dirs = array(
'D:\folder' ;
'D:\folder2' ;
'D:\folder2' ;
'C:\Другое' ;
);
$files = scanDir :: scan ( $dirs );

// Сканировать несколько каталогов на наличие файлов с указанным расширением,
// нет подкаталогов
$files = scanDir :: scan ( $dirs , "jpg" );
//или с массивом расширений
$file_ext = array(
"jpg" ,
"bmp" ,
"png"
);
$files = scanDir :: scan ( $dirs , $file_ext );

// Сканируем несколько каталогов на наличие файлов с любым расширением,
// включаем файлы в рекурсивные подпапки
$files = scanDir::scan ( $dirs , false , true );

// Несколько каталогов с указанными расширениями включают файлы подкаталогов
$files = scanDir::scan ( $dirs , $file_ext , true );
?>

Scandir на стероидах:
На случай, если вы хотите отфильтровать список файлов или просто перечислить несколько уровней подкаталогов.

function dirList ( $path = "" , $types = 2 , $levels = 1 , $aFilter =array()) <
// возвращает массив указанных файлов/каталогов
/ / начать поиск в $path (по умолчанию текущий рабочий каталог)
// return $types: 2 => files; 1 => каталоги; 3 => оба;
// $levels: 1 => смотреть только в $path; 2 => $path и все дочерние элементы;
// 3 => $путь, дети, внуки; 0 => $path и все подкаталоги;
// меньше 0 => дополнение -$levels, ИЛИ все, начиная с -$levels вниз
// например. -1 => все, кроме $path; -2 => все потомки, кроме $path + children
// Оставшиеся аргументы представляют собой фильтрующий массив (список) регулярных выражений, которые работают с полным путем.
// Первый символ (перед '/' в регулярном выражении) '-' => НЕ.
// Первый символ (после возможного '-') 'd' => применяется к имени каталога
// Фильтры могут быть переданы как массив строк или как список строк
// Обратите внимание, что выходные каталоги имеют префикс '*' (делается в строке над возвратом)
$dS = DIRECTORY_SEPARATOR ;
if (!( $path = realpath ( $path ? $path : getcwd ()))) return array(); // неверный путь
// следующая строка избавляет от завершения \ на дисках (работает, поскольку c: == c:\ в PHP). ОК в *nix?
if (substr ($path,-1)==$dS) $path = substr($path,0,-1);
if ( is_null ( $types )) $types = 2 ;
если ( is_null ( $levels )) $levels = 1 ;
if ( is_null ( $aFilter )) $aFilter =array();

// последний аргумент может быть передан как список или как массив
$aFilter = array_slice ( func_get_args (), 3 );
if ( $aFilter && gettype ( $aFilter [ 0 ])== "массив" ) $aFilter = $aFilter [ 0 ];
$adFilter = array();
// теперь перемещаем фильтры каталогов в отдельный массив:
foreach ( $aFilter as $i => $filter ) // для каждого фильтра каталогов.
if (( $pos = stripos (" $filter " , "d" )) && $pos 3 ) < // следующая строка удаляет 'd'
$adFilter [] = substr ( $filter , 0, $pos-1). substr($filter, $pos);
отменить( $aFilter [ $i ]); >
$aFilter = array_merge ( $aFilter ); // сбросить индексы

$aRes = массив(); // результаты, $aAcc является аккумулятором
$aDir = array( $path ); // каталоги для проверки
на наличие ( $i = $levels > 0 ? $levels ++:- 1 ;( $aAcc =array())|| $i --&& $aDir ; $aDir = $aAcc )
while ( $dir = array_shift ( $aDir ))
foreach ( scandir ( $dir ) as $fileOrDir )
if ( $fileOrDir != "." && $fileOrDir != " .." ) <
if ( $dirP = is_dir ( $rp = " $dir$dS$fileOrDir " ))
if ( pathFilter (" $rp$dS " , $adFilter ))
$aAcc [] = $rp ;
if ( $i $levels - 1 && ( $types & ( 2 - $dirP )))
if ( pathFilter ( $rp , $aFilter ))
$aRes [] = ( $dirP ? "*": "" ). $ рп ; >
вернуть $aRes ;
>
?>

пример использования:
( "_" , NULL );
// все файлы, кроме .jpg и .Thumbs.db, будут найдены в каталоге c:\Photo
$aFiles = dirList ( 'c:\Photo' , _ , 0 , '-/\. jpg$/i' , '-/\\\\Thumbs.db$/' );
$aFiles = dirList(); // найти файлы в текущем каталоге
// в следующих строках будут найдены файлы .jpg в подкаталогах, отличных от фотографий, за исключением временных файлов Интернета
set_time_limit ( 60 ); // повторение с верхнего уровня может занять некоторое время
$aFiles = dirList( "c:\\" , _ , 0 , '/\.jpg$/i' , '-d/\\\\Photos ?$/i', '-d/Временный Интернет/i');
?>

Обратите внимание, что эта функция займет много времени при сканировании больших
структур каталогов (что является причиной использования фильтров '[-]d/. /'). ).

Чаба Габор из Вены

scandir() с соответствием регулярному выражению имени файла и параметрами сортировки на основе stat().

функция myscandir ($dir, $exp, $how = 'name', $desc = 0)
<
$r = array();
$dh = @ opendir ( $dir );
if ( $dh ) <
в то время как (( $fname = readdir ( $dh )) !== false ) <
if ( preg_match ( $exp , $fname )) <
$stat = stat("$dir / $fname");
$r [ $fname ] = ( $how == 'имя' )? $fname : $stat [ $how ];
>
>
closedir ( $dh );
if ( $desc ) <
arsort ( $r );
>
else <
сортировать ( $r );
>
>
возврат( array_keys ( $r ));
>

$r = myscandir('./book/', '/^article5\.txt$/i', 'ctime', 1);
print_r ($r);
?>

файлы можно сортировать по имени и атрибутам stat(), по возрастанию и убыванию:

имя имя файла
номер устройства dev
номер инода ino
режим инода режима защиты
nlink количество ссылок
uid userid владельца
gid groupid владельца
тип устройства rdev, если устройство inode *
размер размер в байтах
atime время последнего доступа (отметка времени Unix)
mtime время последней модификации (отметка времени Unix)
ctime время последнего изменения индекса (метка времени Unix)
blksize размер блока файловой системы IO *
blocks количество выделенных блоков

/**
* Эта функция будет рекурсивно сканировать все файлы во вложенной папке и папке.
*
* @author Fazle Elahee
*
*/

if(!empty($lists))
foreach($lists as $f)

$path = "/var/www/SimplejQueryDropdowns";
$file_names = scanFileNameRecursivly($path);

Перемещение по дискам Windows PowerShell и управление элементами на них аналогично управлению файлами и папками на физических дисках Windows. В этой статье обсуждается, как справляться с определенными задачами по работе с файлами и папками с помощью PowerShell.

Список всех файлов и папок в папке

Вы можете получить все элементы непосредственно в папке с помощью Get-ChildItem . Добавьте необязательный параметр Force для отображения скрытых или системных элементов. Например, эта команда отображает прямое содержимое диска C Windows PowerShell (это то же самое, что и физический диск Windows C):

Эта команда выводит список только непосредственно содержащихся элементов, подобно использованию команды DIR Cmd.exe или ls в оболочке UNIX. Чтобы показать содержащиеся элементы, вам также необходимо указать параметр -Recurse. (Это может занять очень много времени.) Чтобы перечислить все на диске C:

Командлет Get-ChildItem может фильтровать элементы с помощью параметров Path, Filter, Include и Exclude, но обычно они основаны только на имени.Вы можете выполнять сложную фильтрацию на основе других свойств элементов с помощью Where-Object .

Следующая команда находит в папке Program Files все исполняемые файлы, последний раз измененные после 1 октября 2005 года и размер которых не меньше 1 МБ и не превышает 10 МБ:

Копирование файлов и папок

Копирование выполняется с помощью Copy-Item . Следующая команда создает резервную копию C:\boot.ini в C:\boot.bak:

Если целевой файл уже существует, попытка копирования завершится неудачно. Чтобы перезаписать уже существующее место назначения, используйте параметр Force:

Эта команда работает, даже если место назначения доступно только для чтения.

Копирование папок работает так же. Эта команда рекурсивно копирует папку C:\temp\test1 в новую папку C:\temp\DeleteMe:

Вы также можете скопировать выбранные элементы. Следующая команда копирует все файлы .txt, содержащиеся где-либо в C:\data, в C:\temp\text :

Вы по-прежнему можете использовать другие инструменты для копирования файловой системы. Объекты XCOPY, ROBOCOPY и COM, такие как Scripting.FileSystemObject, работают в Windows PowerShell. Например, вы можете использовать COM-класс Windows Script Host Scripting.FileSystem для резервного копирования C:\boot.ini в C:\boot.bak :

Создание файлов и папок

Создание новых элементов одинаково работает во всех поставщиках Windows PowerShell. Если поставщик Windows PowerShell имеет несколько типов элементов (например, файловая система поставщика Windows PowerShell различает каталоги и файлы), необходимо указать тип элемента.

Эта команда создает новую папку C:\temp\New Folder:

Эта команда создает новый пустой файл C:\temp\New Folder\file.txt

При использовании переключателя Force с командой New-Item для создания папки, когда папка уже существует, она не перезаписывает папку. Он просто вернет существующий объект папки. Однако если вы используете New-Item -Force для уже существующего файла, файл будет полностью перезаписан.

Удаление всех файлов и папок в папке

Вы можете удалить содержащиеся элементы с помощью команды Remove-Item , но вам будет предложено подтвердить удаление, если элемент содержит что-либо еще. Например, если вы попытаетесь удалить папку C:\temp\DeleteMe, содержащую другие элементы, Windows PowerShell запросит подтверждение перед удалением папки:

Если вы не хотите получать запросы для каждого содержащегося элемента, укажите параметр Recurse:

Отображение локальной папки как диска

Вы также можете сопоставить локальную папку с помощью команды New-PSDrive. Следующая команда создает локальный диск P: с корнем в локальном каталоге Program Files, который виден только из сеанса PowerShell:

Как и в случае с сетевыми дисками, диски, сопоставленные в Windows PowerShell, сразу видны в оболочке Windows PowerShell. Чтобы создать подключенный диск, видимый из Проводника, необходим параметр -Persist. Однако с Persist можно использовать только удаленные пути.

Чтение текстового файла в массив

Командлет Get-Content уже обрабатывает данные, считанные из файла, как массив с одним элементом на строку содержимого файла. Вы можете убедиться в этом, проверив длину возвращаемого контента:

Вывод: используйте Windows PowerShell для отображения файлов в папках и подпапках.

Эй, сценарист! Я старый парень VBScript. Мне нравится использовать VBScript, и я делаю это уже почти 15 лет. Одна из причин, по которой я так люблю VBScript, заключается в том, что он прост в использовании. Это также очень мощно. Но после посещения TechEd за последние несколько лет выяснилось, что Microsoft отказывается от VBScript и использует Windows PowerShell.

Итак, я пытаюсь изучить Windows PowerShell. Что я делаю, так это то, что когда мне нужно внести изменения в существующий сценарий VBScript, я пытаюсь использовать Windows PowerShell для выполнения той же задачи. У меня возникли проблемы с перечислением файлов в папках и вложенных папках. Не могли бы вы подтолкнуть меня в правильном направлении?

Привет, ТиДжей,

Эд Уилсон, специалист по сценариям Microsoft, здесь. Этим утром я потягиваю чашку чая English Breakfast. Я добавил в кастрюлю комбинированную ложку листьев мяты перечной, листьев мяты перечной, корня солодки, плодов шиповника, гибискуса и палочки корицы, пока настаивал ее в течение четырех минут. Чай очень освежает. Говоря об обновлении, я полагаю, что способ, которым Windows PowerShell обрабатывает файлы и папки, станет долгожданным изменением по сравнению с работой, которую вам приходилось выполнять в VBScript.

Сначала позвольте мне сказать, что я не ненавижу VBScript. Я написал три книги о VBScript, и мне было очень весело проводить занятия по VBScript по всему миру, когда я был в MCS.Я не тот человек, который хочет сказать, что вы должны немедленно перейти с VBScript на Windows PowerShell. Смысл сценариев — независимо от языка — в автоматизации. Сценарии — это инструмент, средство для достижения цели, а не сама цель. Поэтому, TJ, я считаю, что вы используете логичный подход к изучению Windows PowerShell и готовитесь к будущему автоматизации Windows.

Но почти 10 лет назад мы опубликовали следующее сообщение: Эй, сценарист! Блог: Как я могу получить список всех файлов в папке и ее подпапках? Скрипт, написанный на VBScript, состоял из 22 строк. Было много работы, чтобы понять, и для кого-то, кто не очень хорошо знаком с VBScript, это было бы довольно запутанным. Вот код — просто для удовольствия.

Список файлов VBScript в папках и подпапках

Установить objFSO = CreateObject("Scripting.FileSystemObject")

Установить objFolder = objFSO.GetFolder(objStartFolder)

Установить colFiles = objFolder.Files

Для каждого objFile в colFiles

Для каждой подпапки в Folder.SubFolders

Установите objFolder = objFSO.GetFolder(Subfolder.Path)

Установить colFiles = objFolder.Files

Для каждого objFile в colFiles

В этом скрипте есть как минимум три проблемы.

Еще одна проблема заключается в том, что независимо от того, нажимаю ли я кнопку "ОК" или красный значок "X" в правом верхнем углу, всплывающие диалоговые окна продолжают появляться. Единственный способ получить контроль над ситуацией (кроме перезагрузки компьютера) — открыть диспетчер задач, найти хост-процесс на основе Windows и убить его. Это показано на следующем изображении:

Третья проблема немного отличается. Это требует некоторого удобства использования, потому что, чтобы изменить назначение сценария, я должен вручную отредактировать сценарий. Это включает в себя открытие сценария в Блокноте (единственный редактор, который мы поставляем для VBScript) и изменение значения ObjStartFolder.

Теперь каждую из этих проблем можно решить, поскольку я могу проверить, выполняется ли сценарий в командной строке через Cscript, и добавить в сценарий входные переменные командной строки. Но оба этих решения потребуют нескольких строк дополнительного кода.

Список файлов в папках и подпапках с помощью PowerShell

Когда дело доходит до работы с файлами и папками и рекурсии по вложенной структуре каталогов, между Windows PowerShell и VBScript возникает почти нечестная конкуренция. Похоже, что команда Windows PowerShell намеренно упростила работу с файлами и папками.

Например, предположим, что в моей музыкальной папке есть ряд вложенных папок, как показано на следующем рисунке. Каждая папка — это музыкальная группа, каждая вложенная папка — это альбом, а внутри каждого альбома — отдельные музыкальные треки.

Список конкретной папки

Для работы с определенной папкой я использую командлет Get-ChildItem. Этот командлет существует со времен Windows PowerShell 1.0, но в более поздних версиях Windows PowerShell он получил пару дополнительных полезных переключателей. Сначала просто укажите конкретную папку:

Get-ChildItem -Path E:\music

Эта команда выводит список всех файлов и папок на уровне E:\music. Эта команда не повторяется по всей структуре. Команда и пример вывода показаны на следующем изображении:

Если я хочу видеть только папки на этом уровне, я использую переключатель –Directory.

Примечание. Параметры –Directory, -File, -Attributes, -Hidden и –System были добавлены в командлет Get-ChildItem в Windows PowerShell 3.0. Эти переключатели доступны через поставщика файловой системы. Дополнительные сведения см. в статье Использование PowerShell для поиска динамических параметров.

Это будет команда для просмотра только каталогов на уровне E:\Music:

Get-ChildItem -Path E:\music –Directory

Чтобы видеть только файлы на этом уровне, я использую переключатель –File:

Get-ChildItem -Path E:\music –File

Используйте переключатель –Recurse

Чтобы углубиться во вложенную структуру папок, мне нужно использовать переключатель –Recurse. Разница очевидна. Например, следующая команда отображает папки в определенной папке:

Get-ChildItem -Path E:\music\Santana

Get-ChildItem -Path E:\music\Santana –Recurse

На следующем изображении я вижу, что сначала вывод такой же — список папок. Затем он берет каждую папку по очереди и отображает файлы из этой папки. Это продолжается до тех пор, пока не будут отображены все файлы во всех вложенных папках.

Если я использую параметр –File, я не получаю исходный список папок:

Get-ChildItem -Path E:\music\Santana -Recurse –File

Легкая сортировка

Одна из по-настоящему замечательных возможностей Windows PowerShell позволяет легко сортировать информацию. Существует даже командлет Sort-Object. Например, если я хочу посмотреть, какие песни самые длинные, я бы использовал эту команду:

Get-ChildItem -Path E:\music\Santana -Recurse -File | длина сортировки – по убыванию

TJ, это все, что нужно для использования Windows PowerShell для отображения списка файлов в папках и подпапках. Присоединяйтесь ко мне завтра, когда я расскажу о других интересных функциях Windows PowerShell.

Я предпочитаю работать с Python, потому что это очень гибкий язык программирования, который позволяет мне легко взаимодействовать с операционной системой. Это также включает в себя функции файловой системы. Чтобы просто перечислить файлы в каталоге, в игру вступают модули os, subprocess, fnmatch и pathlib. Следующие решения демонстрируют, как эффективно использовать эти методы.

Использование os.walk()

Модуль os содержит длинный список методов, которые работают с файловой системой и операционной системой. Одним из них является walk() , который генерирует имена файлов в дереве каталогов, проходя по дереву либо сверху вниз, либо снизу вверх (по умолчанию сверху вниз).

os.walk() возвращает список из трех элементов. Он содержит имя корневого каталога, список имен подкаталогов и список имен файлов в текущем каталоге. В листинге 1 показано, как написать это, используя всего три строки кода. Это работает с интерпретаторами Python 2 и 3.

Листинг 1. Обход текущего каталога с помощью os.walk()

Использование командной строки через подпроцесс

Примечание. Несмотря на то, что это допустимый способ перечисления файлов в каталоге, его не рекомендуется использовать, поскольку он создает возможность для атак путем внедрения команд.

Как уже было описано в статье Параллельная обработка в Python, модуль подпроцесса позволяет выполнять системную команду и собирать ее результат. Системная команда, которую мы вызываем в этом случае, следующая:

Пример 1. Список файлов в текущем каталоге

Команда ls -p . выводит список файлов каталога для текущего каталога и добавляет разделитель / в конце имени каждого подкаталога, который нам понадобится на следующем шаге. Вывод этого вызова передается команде grep, которая фильтрует данные по мере необходимости.

Параметры -v /$ исключают все имена записей, которые заканчиваются разделителем / . На самом деле /$ — это регулярное выражение, которое соответствует всем строкам, содержащим символ / в качестве самого последнего символа перед концом строки, который представлен $ .

Модуль подпроцесса позволяет создавать настоящие каналы и соединять входные и выходные потоки так же, как вы делаете это в командной строке. Вызов метода subprocess.Popen() открывает соответствующий процесс и определяет два параметра с именами stdin и stdout.

В листинге 2 показано, как это запрограммировать. Первая переменная ls определяется как процесс, выполняющий ls -p. который выводит в трубу. Вот почему канал stdout определен как subprocess.PIPE . Вторая переменная grep также определена как процесс, но вместо этого выполняет команду grep -v /$ .

Для чтения вывода команды ls из канала канал стандартного ввода grep определяется как ls.stdout . Наконец, переменная endOfPipe считывает вывод grep из grep.stdout, который выводится в stdout поэлементно в цикле for ниже. Результат показан в Примере 2.

Листинг 2. Определение двух процессов, связанных каналом

Пример 2. Запуск программы

Это решение хорошо работает как с Python 2, так и с Python 3, но можно ли его как-то улучшить? Тогда давайте посмотрим на другие варианты.

Сочетание os и fnmatch

Как вы уже видели, решение с использованием подпроцессов элегантно, но требует большого количества кода. Вместо этого давайте объединим методы из двух модулей os и fnmatch. Этот вариант также работает с Python 2 и 3.

В качестве первого шага мы импортируем два модуля os и fnmatch. Затем мы определяем каталог, в который мы хотели бы вывести список файлов, используя os.listdir() , а также шаблон для фильтрации файлов. В цикле for мы перебираем список записей, хранящихся в переменной listOfFiles .

Наконец, с помощью fnmatch мы фильтруем записи, которые ищем, и выводим соответствующие записи на стандартный вывод. Листинг 3 содержит скрипт Python, а Пример 3 — соответствующие выходные данные.

Листинг 3. Список файлов с использованием модуля os и fnmatch

Пример 3. Вывод листинга 3

Бесплатная электронная книга: Git Essentials

Ознакомьтесь с нашим практическим руководством по изучению Git, включающим передовые практики, общепринятые стандарты и памятку. Перестаньте гуглить команды Git и на самом деле изучите их!

Использование os.listdir() и генераторов

Проще говоря, генератор — это мощный итератор, сохраняющий свое состояние. Чтобы узнать больше о генераторах, ознакомьтесь с одной из наших предыдущих статей — Генераторы Python.

Следующий вариант объединяет метод listdir() модуля os с функцией-генератором. Код работает с версиями 2 и 3 Python.

Как вы уже могли заметить, метод listdir() возвращает список записей для данного каталога. Метод os.path.isfile() возвращает True, если данная запись является файлом. Оператор yield завершает работу функции, но сохраняет текущее состояние и возвращает только имя записи, обнаруженной как файл. Это позволяет нам перебирать функцию-генератор (см. листинг 4). Результат идентичен результату из примера 3.

Листинг 4. Объединение os.listdir() и функции-генератора

Использовать библиотеку путей

Модуль pathlib описывает себя как способ «разбирать, создавать, тестировать и иным образом работать с именами файлов и путями, используя объектно-ориентированный API вместо низкоуровневых строковых операций». Звучит круто - давайте сделаем это. Начиная с Python 3 модуль входит в стандартный дистрибутив.

В листинге 5 мы сначала определяем каталог. Точка ("".") определяет текущий каталог. Затем метод iterdir() возвращает итератор, который выдает имена всех файлов. В цикле for мы печатаем имена файлов один за другим.

Листинг 5. Чтение содержимого каталога с помощью pathlib

Опять же, выходные данные идентичны выходным данным из примера 3.

В качестве альтернативы мы можем извлекать файлы, сопоставляя их имена с помощью чего-то, что называется glob. Таким образом, мы можем получить только те файлы, которые нам нужны. Например, в приведенном ниже коде мы хотим перечислить только файлы Python в нашем каталоге, что мы делаем, указав «*.py» в глобусе.

Листинг 6. Использование pathlib с методом glob

Использование os.scandir()

В Python 3.6 в модуле os становится доступен новый метод. Он называется scandir() и значительно упрощает вызов списка файлов в каталоге.

Сначала импортировав модуль os, используйте метод getcwd() для определения текущего рабочего каталога и сохраните это значение в переменной пути. Затем scandir() возвращает список записей для этого пути, который мы проверяем на наличие файла с помощью метода is_file().

Листинг 7. Чтение содержимого каталога с помощью scandir()

Опять же, вывод листинга 7 идентичен результату из примера 3.

Заключение

Существуют разногласия, какая версия лучше, какая самая элегантная, а какая самая "питоновская". Мне нравится простота метода os.walk(), а также использование модулей fnmatch и pathlib.

Две версии с процессами/конвейерами и итератором требуют более глубокого понимания процессов UNIX и знания Python, поэтому они могут подойти не всем программистам из-за их дополнительной (и ненужной) сложности.

Чтобы найти ответ на вопрос, какая версия самая быстрая, очень удобен модуль timeit. Этот модуль подсчитывает время, прошедшее между двумя событиями.

Чтобы сравнить все наши решения, не изменяя их, мы используем функцию Python: вызываем интерпретатор Python с именем модуля и выполняемым соответствующим кодом Python. Сделать это для всех сценариев Python одновременно помогает сценарий оболочки (Листинг 8).

Листинг 8. Оценка времени выполнения с помощью модуля timeit

Тесты проводились с использованием Python 3.5.3. Результат выглядит следующим образом, тогда как os.walk() дает лучший результат. Выполнение тестов с помощью Python 2 возвращает разные значения, но не меняет порядок — os.walk() по-прежнему находится вверху списка.

Метод Результат для 100 000 000 циклов
os.walk 0,0085 мкс на цикл
подпроцесс/канал 0,00859 мкс на цикл
os.listdir/fnmatch 0,00912 мкс на цикл
os.listdir/generator 0,00867 мкс на цикл
pathlib 0,00854 мкс на цикл
pathlib/glob 0,00858 мкс на цикл цикл
os.scandir 0,00856 мкс на цикл

Благодарности

Автор хотел бы поблагодарить Герольда Рупрехта за его поддержку и комментарии при подготовке этой статьи.

Приведенный выше код написан для рекурсивного поиска CSV-файлов в каталоге и подкаталоге; его можно использовать для поиска файлов любого типа. Вам просто нужно изменить EXT .
Итак, если вы хотите найти все файлы .css, все, что вам нужно сделать, это изменить EXT на .css

Объяснение кода

Теперь давайте построчно. Я постараюсь объяснить каждую строчку кода.

Сначала мы импортируем модули glob и os

    Модуль
  • os предоставляет нам функции, зависящие от операционной системы.
    В нашем случае мы будем использовать его функции walk и path.join. Они будут подробно объяснены в ближайшее время.
  • модуль glob используется для поиска путей, соответствующих определенному шаблону. Таким образом, мы можем сказать ему найти все файлы js, указав что-то вроде *.js .

Далее мы устанавливаем две константы.

  • Константа PATH — это путь к каталогу, внутри которого мы должны выполнить поиск.
  • Константа EXT — это шаблон для расширения, которое мы собираемся искать.

Строка выше, написанная в типичном стиле Python, может быть переписана, как показано ниже.

    os.walk("path/to/some/directory") перечислит все имена файлов в дереве каталогов (каталог и любые подкаталоги внутри него, подкаталоги внутри подкаталога и т. д.).
    Выдает (это генератор) кортеж из 3 элементов – путь к каталогу, список подкаталогов внутри текущего пути и все файлы в каталоге.
    Итак, предположим, что у вас есть следующая структура каталогов.

Для каталога выше код ниже

выведет результат ниже

  • путь/к/какому/каталогу/блогу
  • путь/к/некоторым/каталогам/блогам/контроллерам
  • путь/к/некоторым/каталогам/блогам/моделям

Надеюсь, это объясняет код.

Ссылки

Делись заботой!!

Нам действительно очень приятно слышать от наших читателей. Если у вас есть какие-либо предложения или вопросы или даже жалобы, пожалуйста, свяжитесь с нами. Если вам понравилась эта статья, поделитесь ею или оставьте комментарий.

Читайте также: