I am managing large CSV files (files ranging from 750 Mb to 10+ Gb), parsing their data into PSObjects, then processing each of those objects based on what is required.
I wrote the following script to churn through these files line by line, filter based on one of the data fields, then close the file. The script works but I feel that it could be faster. For instance, it took 4.5 hours to parse a 389k line csv file. Taking that filesize and timeline, it would take over two and a half days to work through just the sorting and filtering of the data!
Before anyone suggests "use a database!", I'm 100% with you and have started the RFC for adding a database server to our network. Unfortunately our CAB only meets quarterly and this wasn't deemed an emergency. So, I'm left without a database solution for at least 2-3 months.
Anyhow, here is the code:
[void][reflection.assembly]::LoadWithPartialName("Microsoft.VisualBasic")
$source = Get-FileName "C:\users\$env.username\Downloads"
$reader = New-Object Microsoft.VisualBasic.FileIO.TextFieldParser $source
$reader.SetDelimiters(",")
While(!$reader.EndOfData)
{
$line = $reader.ReadFields()
$details = [ordered]@{
"Plugin ID" = $line[0]
CVSS = $line[2]
Risk = $line[3]
Host = $line[4]
Protocol = $line[5]
Port = $line[6]
Name = $line[7]
Description = $line[9]
Solution = $line[10]
"Plugin Output" = $line[12]
}
$pluginID = $line[0]
$risk = $line[3]
if ($risk -eq "Critical" -or $risk -eq "High" -or $risk -eq "Medium" -or $risk -eq "Low")
{
$allVulns += New-Object PSObject -Property $details
}
else
{
# Filters data into objects based on their plugin ID
Switch ($pluginID)
{
11936 # OS Identification
{
$11936 += New-Object PSObject -Property $details
break
}
14272 # Open Ports
{
$14272 += New-Object PSObject -Property $details
break
}
20811 # Software Inventory
{
$20811 += New-Object PSObject -Property $details
break
}
54615 # Device Type
{
$54615 += New-Object PSObject -Property $details
break
}
66334 # Missing OS Patches
{
$66334 += New-Object PSObject -Property $details
break
}
}
}
}
# Close the read file
$reader.Close()
Is there a faster, more efficient way for this code to execute?
Thanks!
EDIT: Some sample data by request! This is two of the plugins I'm parsing that don't have endless amounts of Plugin Output data.
Plugin ID,CVE,CVSS,Risk,Host,Protocol,Port,Name,Synopsis,Description,Solution,See Also,Plugin Output
11936,,,None,GCAB-L7-449096R,tcp,0,OS Identification,It is possible to guess the remote operating system.,"Using a combination of remote probes (e.g., TCP/IP, SMB, HTTP, NTP,
SNMP, etc.), it is possible to guess the name of the remote operating
system in use. It is also possible sometimes to guess the version of
the operating system.",n/a,,"
Remote operating system : Microsoft Windows 7 Enterprise Service Pack 1
Confidence level : 100
Method : SMB
The remote host is running Microsoft Windows 7 Enterprise Service Pack 1"
14272,,,None,GCAB-L7-449096R,udp,53125,netstat portscanner (SSH),Remote open ports are enumerated via SSH.,"This plugin runs 'netstat' on the remote machine to enumerate open
ports.
See the section 'plugins options' about configuring this plugin.",n/a,https://en.wikipedia.org/wiki/Netstat,Port 53125/udp was found to be open