我似乎无法弄清楚如何简单地将格式化信息导出到CSV,除非我遍历对象中的每个项目并逐行写入CSV,这需要永远。我可以立即将值导出到CSV,只是在使用我遇到问题的属性字典时。
TestCSV文件的格式为包含IP地址的列。
以下是我所拥有的:
$CSV = "C:\TEMP\OutputFile.csv"
$RX = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.|dot|\[dot\]|\[\.\])){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
$TestCSV = "C:\TEMP\FileWithIPs.csv"
$spreadsheetDataobject = import-csv $TestCSV
$Finding = $spreadsheetDataObject | Select-String $RX
$Props = @{ #create a properties dictionary
LineNumber = $finding.LineNumber
Matches = $finding.Matches.Value
}
$OBJ = New-Object -TypeName psobject -Property $Props
$OBJ | Select-Object Matches,LineNumber | Export-Csv -Path $CSV -Append -NoTypeInformation
答案 0 :(得分:2)
这不会按照书面形式运作。您正在使用Import-CSV
创建具有属性的对象数组。 Select-String
命令要求字符串作为输入,而不是对象。如果您想使用Select-String
,您只需指定文件名,或在文件上使用Get-Content
,并将其传递给Select-String
。如果您想要的是行号和IP,我认为如果不是更好的话,这可能也会起作用:
$CSV = "C:\TEMP\OutputFile.csv"
$RX = "(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(?:\.|dot|\[dot\]|\[\.\])){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
$TestCSV = "C:\TEMP\FileWithIPs.csv"
$spreadsheetDataobject = import-csv $TestCSV
$spreadsheetDataobject |
Where{$_.IP -match $RX} |
Select-Object @{l='Matches';e={$_.IP}},@{l='LineNumber';e={[array]::IndexOf($spreadsheetDataobject,$_)+1}} |
Export-Csv -Path $CSV -Append -NoTypeInformation
编辑: wOxxOm非常正确,这个答案比直接解析文本的开销要大得多。但是,对于那些对PowerShell不熟悉的人来说,它可能更容易理解。
关于$_.IP
,因为您使用Import-CSV
创建了一个对象数组。每个对象都具有基于CSV文件头的属性。 IP
在标题中列为您的一个列,因此每个对象都具有IP
的属性,该属性的值是该记录的IP列中的任何值。
让我为您解释Select
行,然后您会发现将源路径添加为另一列很容易。
我正在做的是使用哈希表定义属性。对于我的例子,我将参考上面显示的第一个。由于它是哈希表,因此以@{
开头,以}
结尾。里面有两个键/值对:
l='Matches'
e={$_.IP}
基本上' l'是标签的简称,而且' e'是表达的缩写。标签确定要定义的属性的名称(这相当于导出时的列标题)。表达式定义分配给属性的值。在这种情况下,我实际上只是将IP列重命名为Matches,因为我为每一行分配的值是IP字段中的任何值。如果您在Excel中打开CSV,请复制整个IP列,将其粘贴到最后,然后将标题更改为匹配,这基本上就是我正在做的事情。因此,要将文件路径添加为列,我们可以使用以下内容向Select
行添加一个哈希表:
@{
l='FilePath'
e={$CSV}
}
添加第三个属性,其名称为FilePath,值为$CSV
中存储的值。更新后的Select
行看起来像这样:
Select-Object @{l='Matches';e={$_.IP}},@{l='LineNumber';e={[array]::IndexOf($spreadsheetDataobject,$_)+1}},@{l='FilePath'e={$CSV}} |
答案 1 :(得分:1)
基于内置CSV cmdlet的任何代码都非常慢,因为为每行上的每个字段创建了对象,并且它在大文件上很明显(例如,来自另一个答案的代码需要900秒来处理9MB文件100k行)。
如果您的输入CSV文件很简单,您可以在不到一秒的时间内将其作为文本处理为100k行文件:
$CSV = .......
$RX = .......
$TestCSV = .......
$line = 0 # header line doesn't count
$lastMatchPos = 0
$text = [IO.File]::ReadAllText($TestCSV) -replace '"http.+?",', ','
$out = New-Object Text.StringBuilder
ForEach ($m in ([regex]"(?<=,""?)$RX(?=""?,)").Matches($text)) {
$line += $m.index - $lastMatchPos -
$text.substring($lastMatchPos, $m.index-$lastMatchPos).Replace("`n",'').length
$lastMatchPos = $m.Index + $m.length
$out.AppendLine('' + $line + ',' + $m.value) >$null
}
if (!(Test-Path $CSV)) {
'LineNumber,IP' | Out-File $CSV -Encoding ascii
}
$out.ToString() | Out-File $CSV -Encoding ascii -Append
代码在不太可能但可能包含匹配IP的情况下删除引用的URL字段。