Но команда dir не имеет простого синтаксиса для поиска в нескольких местах. К примеру, для поиска всех файлов с расширением. doc, хранящихся в накопителях C и D, используется следующая команда:
dir/b/s c:\*.doc d:\*.doc
Синтаксические конструкции становятся еще более сложными в случае поиска нескольких категорий объектов, обозначаемых с помощью символов шаблонов (скажем, всех файлов с расширениями. doc,. xls и. ppt), расположенных в различных местах, так как каждый маршрут и каждую группу подстановочных символов приходится вводить отдельно.
Реализованная в среде PowerShell составная команда Get-ChildItem дает возможность упростить эту задачу. Так, выполнить поиск всех файлов. doc,. xls, и. ppt на накопителях C и D можно с помощью такой команды:
get-childitem c:\*, d:\*
-include *.doc,*.xls,*.ppt -recurse
Первый параметр команды Get-ChildItem представляет собой список путей, по которым будет осуществляться поиск, а параметр -Include определяет список комбинаций подстановочных символов, которые уточняют определение пути. Параметр -Recurse аналогичен параметру /s команды dir.
Знакомство со сценарием Whereis.ps1
Возможности команды Get-ChildItem довольно широки, и тем не менее, на мой взгляд, ее следовало бы оснастить дополнительными функциями. Например, мне хотелось бы обходиться без параметра -Recurse, но так, чтобы при этом система осуществляла поиск по стационарным локальным накопителям, если я не ввел путь с клавиатуры. Итак, я приступил к работе по созданию полнофункционального сценария, дополняющего команду Get-ChildItem несколькими дополнительными возможностями. Результатом этой работы стал сценарий Whereis.ps1. Отмечу, что сценарий Whereis.ps1 не является эквивалентом команды whereis, реализованной в Unix-подобных ОС.
В сценарии Whereis.ps1 применяется следующая синтаксическая конструкция:
Whereis.ps1? Name
[-Path
[-LastWriteTimeRange
[-SizeRange
[-Files] [-Dirs] [-Force] [-DefaultFormat]
Параметр -Name определяет набор подстановочных знаков. Аргументом этого параметра может быть массив. Файлы и каталоги, соответствующие сочетанию подстановочных знаков, включаются в выходные данные сценария. Параметр -Name является единственным необходимым параметром командной строки. Чтобы получить сведения о наборах подстановочных знаков, которые вы можете использовать, в строке приглашения PowerShell введите:
get-help about_wildcard
Поскольку -Name — это позиционный параметр, можно опустить имя параметра (-Name) и ввести только его аргумент, если данный параметр является первым по счету параметром, указанным в командной строке после имени сценария.
Параметр -Path указывает путь, и в качестве его аргумента может быть использован массив. Если не указать этот параметр, Whereis.ps1 будет выполнять поиск по всем локальным накопителям. Параметр -Path тоже является позиционным, поэтому имя параметра (-Path) можно опустить и указать только его аргумент, если данный параметр является вторым по счету в командной строке после имени сценария.
Параметр -LastWriteTimeRange указывает включающий границы диапазон дат; этот аргумент тоже может быть массивом. Элементы, у которых значение свойства LastWriteTime находится внутри указанного диапазона, включаются в выходные данные сценария. Если в качестве аргумента для данного параметра указать строки, Whereis.ps1 попытается преобразовать их в объекты DateTime. Если указать одну дату, Whereis.ps1 будет интерпретировать диапазон дат как «указанная дата или позднее». Если же вы укажете массив, сценарий Whereis.ps1 будет интерпретировать первый элемент массива как дату, которая является самой ранней границей массива дат, а второй элемент — как самую позднюю дату массива. Вы можете задать диапазон типа «ранее, чем»; для этого в качестве первого элемента массива следует указать ноль. В таблице 1 приведены некоторые примеры параметра -LastWriteTimeRange.
Параметр -SizeRange задает включающий границы диапазон размеров, выраженный в байтах. В роли аргумента данного параметра может выступать массив. Файлы, у которых значения свойства Length оказываются в границах диапазона, включаются в выходные данные сценария. Если вы зададите всего одно число, сценарий Whereis.ps1 будет интерпретировать массив, о котором идет речь, как «файлы с размерами не менее указанных». Если указать массив, сценарий Whereis.ps1 будет интерпретировать первый элемент этого массива как порог, определяющий нижнюю границу массива, а второй элемент — как порог, определяющий его верхнюю границу. При задании аргументов для -SizeRange можно также указывать суффиксы-сомножители PowerShell (kb, mb и gb). В таблице 2 представлены некоторые примеры значений для параметра -SizeRange. Надо отметить, что параметр -SizeRange игнорируется в том случае, если пользователь указывает только параметр -Dirs (который я опишу ниже), поскольку каталоги не имеют свойства Length.
Параметр -OneLevel указывает на то, что поиск будет осуществляться в заданных каталогах, но не во вложенных в них подкаталогах. Таким образом, он представляет собой параметр, обратный параметру -Recurse команды Get-ChildItem.
Параметр -Files предписывает сценарию Whereis.ps1 включать в выходные данные файлы, а параметр -Dirs — каталоги. По умолчанию используется параметр -Files. Если вы хотите, чтобы поиск осуществлялся как в файлах, так и в каталогах, используйте параметры -Files и -Dirs в сочетании. Если нужно, чтобы поиск осуществлялся только в каталогах, задействуйте только параметр -Dirs.
Параметр -Force соответствует параметру -Force команды Get-ChildItem. Он предписывает сценарию Whereis.ps1 осуществлять поиск элементов с атрибутами hidden или system.
При использовании параметра -DefaultFormat сценарий Whereis.ps1 будет выводить данные не в пользовательском формате, а как объекты файловой системы. На экране представлен пример выходных данных сценария Whereis.ps1 в пользовательском формате, которые читаются проще, чем выходные данные в виде объектов файловой системы, особенно если получено большое число результатов, но выходные данные в пользовательском формате нельзя использовать в качестве входных данных для других сценариев или составных команд, которые ориентированы на получение объектов файловой системы. Параметр -DefaultFormat помогает предотвратить эту проблему.
Внутри сценария Whereis.ps1
Инструкция в начале сценария определяет параметры командной строки сценария. Обычно в сценариях PowerShell в качестве параметров для сценариев (и других глобальных переменных) я использую имена переменных, содержащие как прописные, так и строчные буквы, но это не обязательно. После инструкции param сценарий определяет функции usage, isNumeric, writeItem и main. Затем Whereis.ps1 вызывает функцию main. Напомним, что в сценариях PowerShell перед вызовом функции ее необходимо определить, поэтому Whereis.ps1 вызывает функцию main лишь в последней строке сценария.
Результатом выполнения функции usage является сообщение с разъяснениями относительно того, что представляет собой сценарий и как его использовать. После этого выполнение сценария завершается. Функция main вызывает функцию usage в том случае, если в командной строке отсутствует параметр -Name или при наличии параметра -Help.
Функция main вызывает функцию isNumeric, чтобы убедиться, что аргументы, указанные в параметре -SizeRange, являются численными. Функция isNumeric с помощью оператора -contains определяет, входит ли тип ее параметров в список численных типов (например, Decimal или Double).
Функция writeItem определяет формат выходных данных сценария. Если в командной строке присутствует параметр -DefaultFormat, функция writeItem просто выдает свой аргумент, который представляет собой объект файловой системы; в противном случае функция выводит отформатированную строку. Для вывода отформатированной строки она использует стандартные коды форматирования строки. NET и оператор -f. Дополнительные сведения о форматировании строк можно найти в статье «Formatting Types» на сайте MSDN (msdn.microsoft.com/en-us/library/fbxft59x.aspx).
Функция main
Функция main, представленная в листинге 1, листинг 1 (окончание) содержит основную часть кода сценария. Первая задача этой функции состоит в том, чтобы проверить, присутствует ли параметр -Name. При отсутствии указанного параметра или при наличии параметра -Help функция main вызывает функцию usage, которая отображает сообщение об использовании сценария и завершает его выполнение.
Далее функция main преобразует переменную $Name в массив; если же переменная уже содержит массив, она остается неизменной. Далее функция с помощью цикла for перебирает все элементы массива. Если элемент массива содержит подстановочный символ *, функция присваивает элементу значение $NULL. Данный этап необходим для того, чтобы предотвратить попытку команды Get-ChildItem, которая выполняется позднее в сценарии, выводить содержимое подкаталогов, вложенных в каталог.
Далее функция main проверяет командную строку на наличие параметра -Path. При отсутствии этого параметра функция с помощью команды Get-WmiObject считывает список локальных стационарных накопителей, как показано во фрагменте A. Следовательно, переменная $Path содержит либо путь или пути, указанные в параметре -Path, либо список локальных стационарных накопителей. Затем функция main преобразует переменную $Path в массив; если переменная уже содержит массив, она остается неизменной.
Как явствует из кода, приведенного во фрагменте B, далее функция с помощью цикла for перебирает элементы массива $Path. В каждом элементе массива проверяется последний символ. Если это символ обратной косой черты (\), функция добавляет к пути подстановочный символ *. Затем функция проверяет два последних символа элемента. Если это не символы \*, функция присоединяет к элементу приведенную комбинацию символов. По завершении цикла for каждый элемент пути заканчивается символами *\. Этот процесс дает нам возможность указывать путь в виде C:\Files, а сценарий интерпретирует путь как C:\Files\*. Данный фрагмент сценария не только облегчает ввод путей с клавиатуры, поскольку сокращается число нажатий на клавиши. Он необходим еще и потому, что функция main использует параметр -Include команды Get-ChildItem. Дополнительные сведения о функции параметра -Include приведены во врезке «Параметр Include составной команды Get-ChildItem».
Далее функция main определяет, имеется ли в командной строке параметр -LastWriteTimeRange. При его отсутствии функция создает двухэлементный массив. В первом элементе функция сохраняет самую раннюю из возможных дат (1 January 0001, 00:00:00), а во втором — самую позднюю из возможных (31 December 9999, 23:59:59). Функция получает самую раннюю и самую позднюю из возможных дат, извлекая статические свойства MinValue и MaxValue типа DateTime.
При наличии в командной строке параметра LastWriteTimeRange функция main преобразует переменную $LastWriteTimeRange в массив; переменная остается неизменной, если она уже содержит массив. Если массив содержит только один элемент, функция присоединяет к концу массива второй элемент, содержащий последнюю возможную дату. Далее функция main выясняет, не является ли первый элемент массива нулем; если это так, функция использует первую возможную дату в качестве первого элемента. Затем функция предпринимает попытку преобразовать оба элемента массива в объекты DateTime, используя статический метод Parse типа DateTime, как показывает код во фрагменте C. Если применение метода Parse приводит к ошибке, запускается блок сценария, следующий после инструкции trap; на экране появляется сообщение об ошибке, и выполнение сценария прекращается. Затем функция проверяет, является ли первая дата более ранней, нежели вторая; при несоблюдении этого условия функция выдает ошибку, и выполнение сценария прекращается.
Далее функция main определяет, существует ли параметр -SizeRange. В случае его отсутствия функция создает двухэлементный массив с нулем в качестве первого элемента и максимальным значением для 64?разрядного неподписанного целого числа (UInt64) в качестве второго элемента. Если же параметр -SizeRange существует, функция преобразует переменную $SizeRange в массив; переменная остается неизменной, если она уже содержит массив. Если массив содержит только один элемент, функция присоединяет к концу массива второй элемент с максимальным значением типа UInt64. Код во фрагменте D показывает, как функция main определяет, содержат ли оба элемента численные значения. Для выполнения этой задачи вызывается функция isNumeric, описанная мною выше. Если один из элементов содержит значение, не являющееся численным, функция возвращает ошибку, и выполнение сценария прекращается. Функция возвращает ошибку и в том случае, если значение первого элемента массива больше, чем значение второго.
Далее функция main должна удостовериться в том, что параметры -Files и -Dirs отсутствуют. Если ни один из них не существует, функция присваивает переменной $Files значение $TRUE. Затем она задает двум счетчикам переменных значения, равные нулю. Первый из этих счетчиков ($count) предназначен для того, чтобы вести счет числу найденных элементов, а второй ($sizes) позволяет аккумулировать размер всех файлов.
На этой стадии функция main проанализировала и подтвердила правильность всех параметров сценария. Теперь она выполняет команду Get-ChildItem. Функция передает по конвейеру выходные данные Get-ChildItem команде ForEach-Object, чтобы та могла применить к каждому объекту дополнительные фильтры. Если параметр -Files существует и свойство PsIsContainer объекта имеет значение False (то есть объект представляет собой файл, а не каталог), функция main проверяет, находятся ли значения свойств объекта LastWriteTime и Length внутри диапазонов дат и размеров соответственно. Если же свойства объекта соответствуют указанным критериям, функция main увеличивает значения переменных $count и $sizes, после чего вызывает функцию writeItem для вывода объекта. Функция main выполняет аналогичные проверки для выявления того, существует ли параметр -Dirs и имеет ли свойство объекта PsIsContainer значение True (иначе говоря, является ли объект каталогом, а не файлом); отличие же в данном случае состоит в том, что она не проверяет диапазон размеров объекта и не увеличивает значение переменной $sizes, так как объекты «каталог» не имеют свойства Length.
Образцы команд
Давайте теперь рассмотрим ряд команд, которые иллюстрируют, как сценарий Whereis.ps1 используется для выполнения различных задач. К примеру, если необходимо выполнить поиск видео- и аудиофайлов на всех локальных накопителях, применяется следующая команда:
whereis.ps1 *.asf, *.avi, *.mov, *.mp3,
*.mp4, *.mpg, *.mpeg, *.qt, *.wav,
*.wm, *.wmv
Обратите внимание: из соображенияй экономии места на странице журнала команды отображаются на нескольких строках; вам же следует вводить их одной строкой. Кроме того, важно, чтобы до и после запятых не было пробелов.
Далее, для поиска всех файлов PowerPoint размером 10 Мбайт и более, которые расположены в каталоге C:\Data и его подкаталогах, используйте команду:
whereis.ps1 *.pp [st]*
C:\Data -sizerange 10 mb
Команда для поиска в каталоге C:\Data файлов, которые были модифицированы на протяжении последних 60 дней:
whereis.ps1 * C:\Data
-daterange ((get-date)
— (new-timespan -days 60)), (get-date)
-onelevel
Если вы хотите удалить все те размещенные в каталоге C:\Logs файлы, которые были модифицированы 30 дней тому назад или ранее, используйте следующую команду:
whereis.ps1 * c:\Logs
-daterange 0, ((get-date)
— (new-timespan -days 30))
-onelevel -defaultformat | remove-item
«Усиленная» команда Get-ChildItem
Встроенная в оболочку PowerShell команда Get-ChildItem сама по себе способна решать многие задачи, но сценарий Whereis.ps1 наделяет ее рядом дополнительных функций. Включите Whereis.ps1 в свой комплект рабочих инструментов, и вы сможете отыскивать необходимые объекты еще быстрее. Более того, вы сможете использовать проиллюстрированные выше концепции построения сценариев для настройки и более эффективного применения составных команд PowerShell в процессе решения стоящих перед вами задач.
Билл Стюарт (bill.stewart@frenchmortuary.com) — системный и сетевой администратор компании French Mortuary, Нью-Мехико
Параметр -Include составной команды Get-ChildItem дает возможность сократить число значений, возвращаемых параметром -Path; при его использовании возвращаются только те элементы указанного пути, которые соответствуют аргументу параметра -Include. Но чтобы параметр -Include выполнил свою функцию, следует указать для параметра -Path шаблон или имя файла. Так, следующая команда не возвратит ни одного результата — даже если у вас имеется каталог C:\Data, содержащий файлы. txt:
get-childitem c:\data -include *.txt
Команда не возвращает результатов потому, что для параметра -Include не задан шаблон: имеется всего лишь имя каталога без шаблона имени файлов. Вам нужно использовать следующую команду:
get-childitem c:\data\* -include *.txt
Если к параметру -Include добавить символ *, действие команды будет распространено и на вложенные каталоги. Рассмотрим, к примеру, следующую команду:
get-childitem c:\data\* -include *
Подстановочный знак * означает, помимо прочего, и каталоги, так что указанная команда сформирует список, состоящий не только из файлов каталога C:\Data; в него будут включены файлы, содержащиеся в каталогах первого уровня, вложенных в каталог C:\Data. Чтобы получить список, состоящий только из элементов каталога C:\Data, опустите параметр -Include или в качестве его аргумента укажите $NULL.