Sak*_*o73 27 powershell powershell-2.0
我想编写一个PowerShell脚本,它将递归搜索目录,但排除指定的文件(例如*.log
,和myFile.txt
),还排除指定的目录及其内容(例如,myDir
下面的所有文件和文件夹myDir
).
我一直在使用Get-ChildItem
CmdLet和Where-Object
CmdLet,但我似乎无法得到这种确切的行为.
Mic*_*ens 54
我喜欢Keith Hill的答案,除了它有一个错误,阻止它递过两个级别.这些命令显示错误:
New-Item level1/level2/level3/level4/foobar.txt -Force -ItemType file
cd level1
GetFiles . xyz | % { $_.fullname }
Run Code Online (Sandbox Code Playgroud)
使用Hill的原始代码,您可以获得:
...\level1\level2
...\level1\level2\level3
Run Code Online (Sandbox Code Playgroud)
这是一个经过修正且稍微重构的版本:
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
}
}
}
Run Code Online (Sandbox Code Playgroud)
通过该错误修复,您可以获得此更正的输出:
...\level1\level2
...\level1\level2\level3
...\level1\level2\level3\level4
...\level1\level2\level3\level4\foobar.txt
Run Code Online (Sandbox Code Playgroud)
我也喜欢ajk的简洁回答,正如他所指出的那样,效率较低.顺便说一下,它效率较低的原因是因为当ajk继续时,Hill的算法在找到修剪目标时停止遍历子树.但是ajk的答案也有一个缺陷,我称之为祖先陷阱.考虑一个这样的路径,它包括两次相同的路径组件(即subdir2):
\usr\testdir\subdir2\child\grandchild\subdir2\doc
Run Code Online (Sandbox Code Playgroud)
将您的位置设置在两者之间,例如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 $_ } ) ) { $_ }
}
Run Code Online (Sandbox Code Playgroud)
我的算法相当简洁,但是像ajk一样,它的效率低于Hill(因为同样的原因:它不会停止遍历修剪目标的子树).但是,我的代码比Hill更重要 - 它可以管道!因此,它适合于过滤器链来制作Get-ChildItem的自定义版本,而Hill的递归算法,通过它自己的故障,不能.ajk的算法也可以适应管道使用,但是指定要排除的项目不是那么干净,嵌入在正则表达式中而不是我使用过的简单项目列表中.
我已将树修剪代码打包到Get-ChildItem的增强版本中.除了我相当缺乏想象力的名字 - Get-EnhancedChildItem - 我很兴奋并将它包含在我的开源Powershell库中.除了树修剪之外,它还包括其他几项新功能.此外,代码设计为可扩展的:如果要添加新的过滤功能,则可以直接执行.本质上,首先调用Get-ChildItem,并将其流水线化为通过命令参数激活的每个连续过滤器.这样的事......
Get-EnhancedChildItem –Recurse –Force –Svn
–Exclude *.txt –ExcludeTree doc*,man -FullName -Verbose
Run Code Online (Sandbox Code Playgroud)
...在内部转换为:
Get-ChildItem | FilterExcludeTree | FilterSvn | FilterFullName
Run Code Online (Sandbox Code Playgroud)
每个过滤器必须符合某些规则:接受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 }
}
}
}
Run Code Online (Sandbox Code Playgroud)
这里唯一的另一个部分是Coalesce-Args函数(在Keith Dahlby的这篇文章中找到),它只是在调用没有指定任何路径的情况下将当前目录发送到管道.
因为这个答案有点冗长,而不是详细介绍这个过滤器,我将感兴趣的读者引用到我最近发表的关于Simple-Talk.com的文章,名为Practical PowerShell:Pruning File Trees and extends Cmdlet,我在其中讨论Get-EnhancedChildItem更长的.不过,我要提到的最后一件事是我的开源库New-FileTree中的另一个函数,它允许您生成用于测试目的的虚拟文件树,以便您可以使用上述任何算法.当你在试验其中的任何一个时,我建议% { $_.fullname }
像第一个代码片段中那样进行管道处理,以便检查更有用的输出.
Kei*_*ill 25
Get-ChildItem cmdlet有一个-Exclude
很有吸引力的参数,但它不能用于从我所知道的过滤掉整个目录.尝试这样的事情:
function GetFiles($path = $pwd, [string[]]$exclude) { foreach ($item in Get-ChildItem $path) { if ($exclude | Where {$item -like $_}) { continue } if (Test-Path $item.FullName -PathType Container) { $item GetFiles $item.FullName $exclude } else { $item } } }
ajk*_*ajk 12
这是另一种选择,效率较低但更简洁.这就是我通常处理这类问题的方法:
Get-ChildItem -Recurse .\targetdir -Exclude *.log |
Where-Object { $_.FullName -notmatch '\\excludedir($|\\)' }
Run Code Online (Sandbox Code Playgroud)
该\\excludedir($|\\)'
表达式允许您排除目录及其内容在同一时间.
更新:请通过这种方法检查msorens的优秀答案是否存在边缘情况缺陷,以及整体更加充实的解决方案.
归档时间: |
|
查看次数: |
98423 次 |
最近记录: |