我有一个源树,例如c:\ s,其中包含许多子文件夹。子文件夹之一称为“c:\ s \ Includes”,可以递归包含一个或多个.cs文件。
我想确保c:\ s \ Includes ...路径中的所有.cs文件都没有递归地存在于c:\ s下的任何其他文件夹中。
我编写了下面的PowerShell脚本,该脚本可以工作,但是我不确定是否有更简便的方法。我使用PowerShell的经验不足24小时,因此我觉得有更好的方法。
我可以假设至少使用了PowerShell 3。
我会接受任何可以改善我的脚本的答案,但是我将等待几天才能接受答案。当我说“改善”时,是指它变得更短,更优雅或性能更好。
任何人的任何帮助将不胜感激。
当前代码:
$excludeFolder = "Includes"
$h = @{}
foreach ($i in ls $pwd.path *.cs -r -file | ? DirectoryName -notlike ("*\" + $excludeFolder + "\*")) { $h[$i.Name]=$i.DirectoryName }
ls ($pwd.path + "\" + $excludeFolder) *.cs -r -file | ? { $h.Contains($_.Name) } | Select @{Name="Duplicate";Expression={$h[$_.Name] + " has file with same name as " + $_.Fullname}}
最佳答案
1个
我凝视了一段时间,决定不研究现有答案就写出来,但是我已经瞥了一眼Matt提到Group-Object
的答案的第一句话。经过一些不同的方法后,我得到的答案基本相同,除了他是长而健壮的正则表达式字符转义和设置变量外,我的语言很简洁,因为您要求的答案更短,而且更有趣。
$inc = '^c:\\s\\includes'
$cs = (gci -R 'c:\s' -File -I *.cs) | group name
$nopes = $cs |?{($_.Group.FullName -notmatch $inc)-and($_.Group.FullName -match $inc)}
$nopes | % {$_.Name; $_.Group.FullName}
输出示例:
someFile.cs
c:\s\includes\wherever\someFile.cs
c:\s\lib\factories\alt\someFile.cs
c:\s\contrib\users\aa\testing\someFile.cs
这个概念是:
编辑:我在
^
中添加了$inc
,以使其必须在字符串的开头进行匹配,因此对于不匹配的路径,正则表达式引擎可能会更快地失败。也许这算是过早的优化。2
经过相当密集的尝试之后,更简洁的答案变得容易得多:
写的时间更长,但速度却快得多(不过,它运行速度较慢),我想对于不知道它能做什么的人更容易阅读。
$sourceTree = 'c:\\s'
$allFiles = Get-ChildItem $sourceTree -Include '*.cs' -File -Recurse
$includeFiles = $allFiles | where FullName -imatch "$($sourceTree)\\includes"
$otherFiles = $allFiles | where FullName -inotmatch "$($sourceTree)\\includes"
foreach ($incFile in $includeFiles) {
foreach ($oFile in $otherFiles) {
if ($incFile.Name -ieq $oFile.Name) {
write "$($incFile.Name) clash"
write "* $($incFile.FullName)"
write "* $($oFile.FullName)"
write "`n"
}
}
}
3
因为代码高尔夫很有趣。如果哈希表速度更快,那么经过更少测试的单行代码又会如何呢?
$h=@{};gci c:\s -R -file -Filt *.cs|%{$h[$_.Name]+=@($_.FullName)};$h.Values|?{$_.Count-gt1-and$_-like'c:\s\includes*'}
编辑:对此版本的解释:它的解决方法与版本1大致相同,但是分组操作在哈希表中明确进行。哈希表的形状变为:
$h = {
'fileA.cs': @('c:\cs\wherever\fileA.cs', 'c:\cs\includes\fileA.cs'),
'file2.cs': @('c:\cs\somewhere\file2.cs'),
'file3.cs': @('c:\cs\includes\file3.cs', 'c:\cs\x\file3.cs', 'c:\cs\z\file3.cs')
}
它会为所有.cs文件命中一次磁盘,并迭代整个列表以构建哈希表。我不认为它能做的比这少。
它使用
+=
,因此可以将文件添加到该文件名的现有数组中,否则它将覆盖每个哈希表列表,并且对于最近访问的文件,它们将是一个长项。它使用
@()
-因为当它第一次到达文件名时,$h[$_.Name]
将不会返回任何内容,并且脚本首先需要将一个数组而不是字符串放入哈希表中。如果它是+=$_.FullName
,那么第一个文件将作为字符串进入哈希表,而下次+=
将进行字符串连接,这对我没有用。这通过强制每个文件成为一个单项数组来强制哈希表中的第一个文件启动数组。获得此结果的最少代码方法是使用+=@(..)
,但是为每个文件创建一次性数组的工作是不必要的。也许将其更改为更长的代码,这将减少数组创建的工作量?更改部分
%{$h[$_.Name]+=@($_.FullName)}
像
%{if (!$h.ContainsKey($_.Name)){$h[$_.Name]=@()};$h[$_.Name]+=$_.FullName}
(我猜想,对于最有可能是缓慢的PowerShell代码,并且没有经过测试,我没有太多的直觉。)
之后,使用
h.Values
不会再次遍历每个文件,而是遍历哈希表中的每个数组-每个唯一文件名一个。一定要检查数组大小并修剪非重复项,但是-and
操作会短路-当Count -gt 1
失败时,右侧检查路径名的位不会运行。如果数组中有两个或多个文件,则执行
-and $_ -like ...
并进行模式匹配,以查看includes
路径中是否至少有一个重复项。 (错误:如果所有重复项都在c:\cs\includes
中,并且在其他任何地方都没有,它将仍然显示它们)。-
4
这是经过编辑的版本3,具有哈希表初始化调整功能,现在它跟踪$ s中的可见文件,然后仅考虑多次看到的文件。
$h=@{};$s=@{};gci 'c:\s' -R -file -Filt *.cs|%{if($h.ContainsKey($_.Name)){$s[$_.Name]=1}else{$h[$_.Name]=@()}$h[$_.Name]+=$_.FullName};$s.Keys|%{if ($h[$_]-like 'c:\s\includes*'){$h[$_]}}
假设它起作用了,那就是它的作用。
-
编辑主题分支;我一直认为应该有一种方法可以对System.Data命名空间中的内容进行处理。有人知道您是否可以在不增加样板的情况下将
System.Data.DataTable().ReadXML()
连接到gci | ConvertTo-Xml
吗?
关于powershell - 如何使用Powershell列出其中一个文件夹中存在的文件夹结构中的重复文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27066989/