【问题标题】:Powershell to output a list of records in a txt filePowershell在txt文件中输出记录列表
【发布时间】:2020-09-23 17:53:17
【问题描述】:

在 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"

基于 write-host 命令的 Powershell 工作正常。 它给出了每个 CSV 文件名以及每个文件上的行数

输出问题: 我只看到 output.txt 文件中的第一个文件名

输出显示: 仅第一个文件的文件名和 # 行

目标: output.txt 是否可以列出所有文件名以及相应的可能行数

谢谢

【问题讨论】:

  • 请注意,除非您在 8.3 文件命名方面遇到问题,否则 Get-Content-filter 将比使用 -include 更快。还要小心使用 Get-Content 执行此任务,因为它不会考虑可能的多行记录,将这些记录计为多个。最准确但可能最慢的方法是使用Import-CsvMeasure-Object

标签: powershell calculated-property


【解决方案1】:

您的问题是,您错误地尝试在 foreach 循环之后构建结果数组 ($listofFiles += ...),而不是 在其中 - 正确缩进源代码,问题会更加明显。

此外,您可以大大简化解决方案。

$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
  }
}

ForEach-Object 命令中手动构造[pscustomobject] 的替代方法是将Select-Objectcalculated properties 一起使用,如TheMadTechnician's answer 所示。也就是说,考虑到在当前情况下,您需要 两个 计算属性 - 一个用于计算行数,一个用于将 Name 属性重命名为 File Name,添加的详细程度为可能不值得。

警告:正如Compo 所说,如果您的 CSV 文件有可能包含跨越多行的单独行(这种情况很少见),请使用(Import-Csv $_.FullName | Measure-Object).Count计算Number of Rows 字段,但请注意这会更慢。

注意:

  • 如果您的 CSV 文件小到足以放入整个内存(一次一个),您可以使用 (Get-Content $_.FullName).Count - 1 计算行数来加快您的命令速度。

  • 请参阅this answer,了解为什么不建议使用+= 迭代“扩展”数组,以及如何将foreach 循环用作表达式,其多个输出可以直接收集在通过分配给变量的数组。

【讨论】:

    【解决方案2】:

    类似于 mklement0 的回答,但我想我只是将信息添加到您已经获得的现有文件信息中。

    $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
    

    【讨论】:

      【解决方案3】:

      您对变量$files 似乎是Get-Content,但迭代的是不同的数组。

      Write-Host 对您有用,因为它位于循环内部。

      您的追加似乎在您的循环之外,并且您正在调用一个您未构建的文件(至少在提供的摘录中)。

      如果您使用格式标准,则更容易注意到诸如脱离循环之类的事情。

      正如 Compo 所提到的,有一些更快的方法可用于实现您所寻求的目标。

      $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'
      

      【讨论】:

      • 你如何测量这里的行数?我没有看到这个答案
      • 啊,你是对的,我似乎排除了那一点。您可以使用 csv 对象上的 Count 属性来获取行。我已将其添加到答案中。
      猜你喜欢
      • 2021-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多