Export-CSV cmdlet在FOREACH语句的每次迭代期间重写整个CSV

时间:2013-06-04 18:59:39

标签: powershell csv perfmon

我正在使用一些代码,这些代码将采用一系列性能计数器,然后将计数器放在.csv文件中,每次达到1MB时都会翻转。

$Folder="C:\Perflogs\BBCRMLogs" # Change the bit in the quotation marks to whatever directory you want the log file stored in

$Computer = $env:COMPUTERNAME
$1GBInBytes = 1GB
$p = LOTS OF COUNTERS;

# If you want to change the performance counters, change the above list. However, these are the recommended counters for a client machine. 

$num  = 0
$file = "$Folder\SQL_log_${num}.csv"

if( !(test-path $folder)) {New-Item $Folder -type directory}

Get-Counter -counter $p -SampleInterval 2 -Continuous | Foreach {

    if ((Get-Item $file -ErrorAction SilentlyContinue ).Length -gt 1mb) 
    {
        $num +=1
        $file = "$Folder\SQL_log_${num}.csv"
    }

    $_

} | Foreach-Object { $_ | Export-Csv $file -Force -Append}

现在,它运作良好。迭代工作正常,每次.csv达到1MB时都会创建一个新文件。但是,第一个之后的每个.CSV在2分钟后已经创建为1MB,从而导致创建一个新文件。我不太确定为什么会发生这种情况,尽管我认为这是因为Powershell每次创建时都会重写整个.csv。

2 个答案:

答案 0 :(得分:1)

将最后一行更改为此行,将每行转换为CSV格式,然后将其附加到输出文件中:

} | Foreach-Object {($_ | ConvertTo-Csv -NoTypeInformation)[1] | Out-File $file -Append -Encoding ASCII}.

一些注意事项:

  • -Encoding ASCII并非绝对必要,但在某些应用程序中可能无法使用Unicode CSV文件(例如,默认情况下,Excel不会将其作为CSV文件打开,所有内容都将在A列)
  • ($_ | ConvertTo-Csv -NoTypeInformation)[1]中索引的原因是ConvertTo-Csv -NoTypeInformation每次仍然输出标题行,因此您想要抓住双行输出的第二行(($_ | ConvertTo-Csv -NoTypeInformation)[0]是标题行)
  • 由于您没有输出标题行,因此您需要在循环之前将一个输出到$ file

答案 1 :(得分:1)

[我将此作为新答案发布而不是编辑原文,因为它完全不同。替换或附加原始答案将使随后的讨论混乱。]

您需要做的是使用正则表达式从Get-Counter输出的Readings属性中提取值,并从时间戳和这些值手动构造CSV输出。将最后一行更改为此(根据您的首选样式格式化):

| %{'"' + (Get-Date $_.Timestamp -f 's') + '","' + (([regex]::matches($_.Readings, '(?<=\\\\.+?:\n)(.+?)(?=\n)') | select -ExpandProperty Value) -join '","') + '"'} | Out-File $file -Append -Encoding ASCII

要打破这一点:

  • (Get-Date $_.Timestamp -f 's')这部分并非绝对必要,但我认为这会让您的结果更容易理解。 's'格式将日期置于ISO 8601可排序模式中。您可以将“u”替换为另一种可排序格式,或使用您喜欢的自定义格式字符串。或者只需将其替换为 $ _。时间戳以保留原始格式。
  • [regex]::matches($_.Readings, '(?<=\\\\.+?:\n)(.+?)(?=\n)')正则表达式匹配以 \\ 开头并以结尾的行前面的任何行的内容(那些讨厌的计数器名称)你想摆脱)。请注意,我使用 [regex] :: matches ,执行全局匹配,而不是 [regex] :: match -match ,它只会为您提供每个字符串的第一个匹配项(Readings属性是一个字符串,因此只返回第一个计数器读数)。
  • | select -ExpandProperty Value生成所有匹配项的数组,然后您可以使用“,”加入,并使用包围以生成CSV输出。

由于您没有使用转换功能,因此还需要构建标题行。在管道正上方添加此行:

`'"Timestamp","' + ($p -join '","') + '"' | Out-File $file -Append -Encoding ASCII`

假设$ p是一个数组(它应该是)。如果它是一个字符串,那么根据格式你可以按原样使用它,或者 -split 它并以CSV格式重新加入它。