Я хочу написать PowerShell script, который будет рекурсивно искать в каталоге, но исключать указанные файлы (например, *.log
и myFile.txt
), а также исключать указанные каталоги и их содержимое (например, myDir
и все файлы и папки ниже myDir
).
Ответ 2
Мне нравится ответ Кейта Хилла, за исключением того, что у него есть ошибка, которая препятствует повторению двух предыдущих уровней. Эти команды обнаруживают ошибку:
New-Item level1/level2/level3/level4/foobar.txt -Force -ItemType file
cd level1
GetFiles . xyz | % { $_.fullname }
С исходным кодом Hill вы получите следующее:
...\level1\level2
...\level1\level2\level3
Вот исправленная и слегка реорганизованная версия:
function GetFiles($path = $pwd, [string[]]$exclude)
{
foreach ($item in Get-ChildItem $path)
{
if ($exclude | Where {$item -like $_}) { continue }
$item
if (Test-Path $item.FullName -PathType Container)
{
GetFiles $item.FullName $exclude
}
}
}
С этим исправлением ошибок вы получите этот исправленный вывод:
...\level1\level2
...\level1\level2\level3
...\level1\level2\level3\level4
...\level1\level2\level3\level4\foobar.txt
Мне также нравится ответ ajk для краткости, хотя, как он указывает, он менее эффективен. Причина, по которой она менее эффективна, кстати, заключается в том, что алгоритм Хилла останавливает перемещение поддерева, когда он находит черносливную мишень, пока айк продолжается. Но ответ айка также страдает от недостатка, который я называю ловушкой предков. Рассмотрим такой путь, который включает в себя один и тот же компонент пути (т.е. Subdir2) дважды:
\usr\testdir\subdir2\child\grandchild\subdir2\doc
Установите свое местоположение где-то посередине, например. cd \usr\testdir\subdir2\child
, затем запустите алгоритм ajk, чтобы отфильтровать нижний subdir2
, и вы не получите никакого вывода вообще, т.е. он отфильтровывает все из-за наличия subdir2
выше в пути. Тем не менее, это угловой случай, и его вряд ли ударят часто, поэтому я не исключаю решение ajk из-за этой проблемы.
Тем не менее, я предлагаю здесь третью альтернативу, которая не имеет ни одной из этих двух ошибок. Вот базовый алгоритм, содержащий определение удобства для пути или путей для обрезки - вам нужно только изменить $excludeList
на свой собственный набор целей, чтобы использовать его:
$excludeList = @("stuff","bin","obj*")
Get-ChildItem -Recurse | % {
$pathParts = $_.FullName.substring($pwd.path.Length + 1).split("\");
if ( ! ($excludeList | where { $pathParts -like $_ } ) ) { $_ }
}
Мой алгоритм достаточно лаконичен, но, как и ajk, он менее эффективен, чем Hill (по той же причине: он не останавливает перемещение поддеревьев по черновикам). Тем не менее, мой код имеет важное преимущество перед Hill's - он может конвейер! Поэтому можно вписаться в цепочку фильтров, чтобы создать пользовательскую версию Get-ChildItem, в то время как рекурсивный алгоритм Hill, по своей собственной ошибке, не может. Алгоритм ajk также может быть адаптирован к использованию конвейера, но указание элемента или элементов для исключения не так чисто, что оно встроено в регулярное выражение, а не в простой список элементов, которые я использовал.
Я упаковал свой код обрезки деревьев в расширенную версию Get-ChildItem. Помимо моего довольно невообразимого имени - Get-EnhancedChildItem - Я взволнован и включил его в свой библиотека Powershell с открытым исходным кодом. Он включает в себя несколько других новых возможностей, кроме обрезки деревьев. Кроме того, код предназначен для расширения: если вы хотите добавить новую возможность фильтрации, это просто сделать. По сути, Get-ChildItem вызывается первым и конвейерируется в каждый последующий фильтр, который вы активируете с помощью параметров команды. Таким образом, что-то вроде этого...
Get-EnhancedChildItem –Recurse –Force –Svn
–Exclude *.txt –ExcludeTree doc*,man -FullName -Verbose
... преобразуется внутри:
Get-ChildItem | FilterExcludeTree | FilterSvn | FilterFullName
Каждый фильтр должен соответствовать определенным правилам: принимать объекты FileInfo и DirectoryInfo в качестве входов, генерировать то же самое, что и выходы, и использовать stdin и stdout, чтобы он мог быть вставлен в конвейер. Вот тот же код, который был реорганизован для соответствия этим правилам:
filter FilterExcludeTree()
{
$target = $_
Coalesce-Args $Path "." | % {
$canonicalPath = (Get-Item $_).FullName
if ($target.FullName.StartsWith($canonicalPath)) {
$pathParts = $target.FullName.substring($canonicalPath.Length + 1).split("\");
if ( ! ($excludeList | where { $pathParts -like $_ } ) ) { $target }
}
}
}
Единственной дополнительной частью здесь является функция Coalesce-Args (найденная в этот пост by Keith Dahlby), которая просто отправляет текущий каталог вниз в том случае, если в вызове не указаны какие-либо пути.
Поскольку этот ответ становится несколько длинным, а не углубляться в подробности этого фильтра, я отсылаю заинтересованного читателя к моей недавно опубликованной статье на Simple-Talk.com под названием Практическая PowerShell: обрезка файлов деревьев и расширение командлетов, где я обсуждаю Get-EnhancedChildItem еще большую длину. Тем не менее, последнее, что я упомянул, - это еще одна функция в моей библиотеке с открытым исходным кодом, New-FileTree, которая позволяет генерировать фиктивное дерево файлов для поэтому вы можете использовать любой из вышеперечисленных алгоритмов. И когда вы экспериментируете с любым из них, я рекомендую использовать трубку до % { $_.fullname }
, как это было в самом первом фрагменте кода, для более полезного вывода для проверки.