Powershell to output a list of records in a txt file

744 Views Asked by At

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

3

There are 3 best solutions below

0
On BEST ANSWER

Similar to mklement0's answer, but I think I'd just add the information to the existing file info you're already getting.

$listOfFiles = Get-ChildItem C:\temp -Recurse -Filter *.csv | Select-Object *,@{l='Number of Rows';e={(Get-Content $_.FullName | Measure-Object -Line).Lines - 1}}
$listOfFiles | Select Name,'Number of Rows' | Export-Csv 'c:\test\output.csv' -NoTypeInformation
0
On

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 - 1 to calculate the row count.

  • See this answer for why iteratively "extending" arrays with += is ill-advised, and how even foreach loops can be used as expression whose multiple outputs can directly be collected in an array by assigning to a variable.

2
On

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'