'Powershell to output a list of records in a txt file
Running the following in powershell
$files = Get-ChildItem -path "C:\temp" -Recurse -Include "*.csv"
ForEach($BusinessFile in $BusinessFiles)
{
$bfiles = get-content $BusinessFile.fullname | Measure-Object -line
$bRowsinFile = $bfiles.lines -1
write-host "Business File Name: " $BusinessFile.name
Write-host "Number of Rows: " $bRowsinFile -ForegroundColor Yellow
}
$records =@{
"File Name" = $businessFile.Name
"Number of Rows" = $bRowsinFile
}
}
$listofFiles = @()
$listofFiles += New-Object -TypeName PSObject -Property $records
$listofFiles | Out-File "c:\test\output.txt"
Invoke-Item "c:\test\output.txt"
Powershell based on the write-host command is working properly. It gives each CSV file name along with # of rows on each file
Output issue: I only see the first file name in the output.txt file
Output shows: File name and # rows for the first file only
Goal: Is it possible for the output.txt to list all file names with the corresponding # of rows possible
Thanks
Solution 1:[1]
Your problem is that you're mistakenly trying to build up the result array ($listofFiles += ...) after the foreach loop instead of inside it - with proper indentation of your source code that problem would have been more obvious.
Additionally, you can greatly streamline the solution.
$listOfFiles = Get-ChildItem C:\temp -Recurse -Filter *.csv | ForEach-Object {
[pscustomobject] @{
'File Name' = $_.Name
'Number of Rows' = (Get-Content $_.FullName | Measure-Object -Line).Lines - 1
}
}
The alternative to constructing a [pscustomobject] manually inside a ForEach-Object command is to use Select-Object with calculated properties, as shown in TheMadTechnician's answer. That said, given that in the case at hand you'd need two calculated properties - one to calculate the number of lines and one to rename the Name property to File Name, the added verbosity is probably not worth it.
Caveat: As Compo notes, if there's a chance that your CSV files contain individual rows that span multiple lines (which is rare), use (Import-Csv $_.FullName | Measure-Object).Count to calculate the Number of Rows field, but note that this will be slower.
Note:
If your CSV files are small enough to fit into memory as a whole (one at a time), you can speed up your command by using
(Get-Content $_.FullName).Count - 1to calculate the row count.See this answer for why iteratively "extending" arrays with
+=is ill-advised, and how evenforeachloops can be used as expression whose multiple outputs can directly be collected in an array by assigning to a variable.
Solution 2:[2]
You appear to Get-Content to the variable $files, but iterate over a different array.
The Write-Host is working for you because it's inside of the loop.
Your append appears to be outside of your loop, and you are invoking a file that you did not build (at least in the provided excerpt).
If you use formatting standards, it is easier to notice things like falling outside of a loop.
And as Compo mentioned, there are some faster methods that can be used to reach the goal you are seeking.
$businessFiles = Get-ChildItem -Path 'C:\temp\' -Recurse -Filter '*.csv'
[System.Collections.ArrayList]$listOfFiles = @()
foreach ($businessFile in $businessFiles) {
$file = Import-Csv $BusinessFile.fullname
$businessNumRows = $file.Count
Write-Host 'Business File Name: ' $businessFile.name
Write-Host 'Number of Rows: ' $businessNumRows -ForegroundColor Yellow
$record = @{
'File Name' = $businessFile.Name
'Number of Rows' = $businessNumRows
}
$recordObj = New-Object -TypeName PSObject -Property $record
$listOfFiles.Add($recordObj) > $null
}
$listOfFiles | Export-Csv 'c:\test\output.csv' -NoTypeInformation
Invoke-Item 'c:\test\output.csv'
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 |
