如何用PowerShell获取目录文件并在另一目录搜索后导出CSV?
问题:使用PowerShell批量搜索文件并导出CSV结果
我需要用PowerShell实现以下需求:
- 获取指定目录(比如C:\Temp)下的所有文件名及完整路径
- 逐个在另一目标目录(比如C:\Filesnew)中搜索这些文件
- 导出包含以下字段的CSV文件:
- Filename:原文件名
- Filepath:原文件的完整路径
- Found?:是否找到(y/n)
- FoundIn:找到时的完整路径,未找到则为空
示例场景
原目录C:\Temp下的文件:
- C:\Temp\Test2\Test.docx
- C:\Temp\Test\test.txt
- C:\Temp\Test\file.html
期望导出的CSV格式:
| Filename | Filepath | Found? | FoundIn |
|---|---|---|---|
| Test.docx | C:\Temp\Test2\Test.docx | y | C:\Filesnew\TransferedFiles\Test.docx |
| test.txt | C:\Temp\Test\test.txt | n | |
| file.html | C:\Temp\Test\file.html | y | C:\Filesnew\webfiles\file.html |
我当前的代码卡在这儿,求帮忙修正:
$files = gci -r * | select-object name, fullname foreach ($file in $files) { gci -path C:\Filesnew -recurse | where {$_.Name -match $file.Name} | select-object $files.Name $files.fullname, $file.fullname | Export-CSV C:\test.csv }
解决方案
原代码的问题分析
- 效率极低:循环内每次都递归遍历目标目录,文件数量多的时候会重复做大量无用功
- 语法错误:
select-object的参数写法错误,变量引用混乱,没有构建需要的字段 - 结果覆盖:每次循环都调用
Export-CSV,默认会覆盖之前的结果,最终只保留最后一个文件的搜索记录 - 匹配风险:使用
-match会把文件名当作正则表达式处理,比如test.txt会意外匹配test1.txt这类文件
修正后的代码
# 定义原目录、目标目录和输出CSV路径 $sourceDir = "C:\Temp" $targetDir = "C:\Filesnew" $outputCsv = "C:\test.csv" # 获取原目录下所有文件的完整列表(只取文件,排除目录) $sourceFiles = Get-ChildItem -Path $sourceDir -Recurse -File # 预先遍历目标目录,构建文件名到路径的映射表,提升搜索效率 $targetFileMap = @{} Get-ChildItem -Path $targetDir -Recurse -File | ForEach-Object { # 若目标目录有重名文件,这里会保留最后一个,可根据需求调整逻辑 $targetFileMap[$_.Name] = $_.FullName } # 批量生成结果对象 $results = foreach ($file in $sourceFiles) { $foundPath = $targetFileMap[$file.Name] [PSCustomObject]@{ Filename = $file.Name Filepath = $file.FullName 'Found?' = if ($foundPath) { 'y' } else { 'n' } FoundIn = $foundPath ?? '' } } # 一次性导出所有结果到CSV $results | Export-Csv -Path $outputCsv -NoTypeInformation -Encoding UTF8
代码说明
- 预构建映射表:只遍历一次目标目录,把文件名和对应路径存入哈希表,后续直接通过键值查找,效率提升明显
- 精准匹配:用文件名直接作为哈希表的键,避免正则匹配的意外问题
- 正确构建字段:通过
PSCustomObject严格定义CSV需要的四个字段,确保输出格式符合预期 - 避免结果覆盖:先收集所有结果,再一次性导出到CSV,保证所有记录都被保存
处理目标目录重名文件的版本
如果目标目录存在多个同名文件,可修改代码将所有匹配路径用分号分隔:
# 定义路径 $sourceDir = "C:\Temp" $targetDir = "C:\Filesnew" $outputCsv = "C:\test.csv" $sourceFiles = Get-ChildItem -Path $sourceDir -Recurse -File # 构建文件名到路径数组的映射 $targetFileMap = @{} Get-ChildItem -Path $targetDir -Recurse -File | ForEach-Object { if (-not $targetFileMap.ContainsKey($_.Name)) { $targetFileMap[$_.Name] = @() } $targetFileMap[$_.Name] += $_.FullName } $results = foreach ($file in $sourceFiles) { $foundPaths = $targetFileMap[$file.Name] [PSCustomObject]@{ Filename = $file.Name Filepath = $file.FullName 'Found?' = if ($foundPaths) { 'y' } else { 'n' } FoundIn = if ($foundPaths) { $foundPaths -join ';' } else { '' } } } $results | Export-Csv -Path $outputCsv -NoTypeInformation -Encoding UTF8
内容的提问来源于stack exchange,提问作者rdkli
相关产品推荐
相关产品推荐

