1
我盯着这个看了一会儿,决定不研究现有答案就写出来,但我已经看了一眼马特答案的第一句话提到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
概念是:
- 获取整个源代码树中的所有 .cs 文件
- 将它们分成 {filename: {files which share this filename}} 的组
- 对于每个组,仅保留那些文件集包含路径与包含文件夹匹配的任何文件以及包含路径与包含文件夹不匹配的任何文件的文件。这一步涵盖
- 重复(如果文件仅在无法通过两个测试时才存在)
- 在 {includes/not-includes} 划分中重复,而不是在一个分支中重复
- 也可以处理三次、n 次重复。
编辑:我将^ 添加到$inc 以表示它必须在字符串的开头匹配,因此对于不匹配的路径,正则表达式引擎可能会更快地失败。也许这算作过早优化。
2
在相当密集的尝试之后,更清晰的答案的形状要容易得多:
- 获取所有文件,将它们拆分为包含、不包含数组。
- 嵌套 for 循环测试每个文件与其他所有文件。
更长,但非常写起来更快(虽然运行速度更慢),我想对于不知道它做什么的人来说更容易阅读。
$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?