CSV 脚本的 PowerShell 获取计数器,编码问题

Posted

技术标签:

【中文标题】CSV 脚本的 PowerShell 获取计数器,编码问题【英文标题】:PowerShell Get-Counter to CSV Script, Encoding Problem 【发布时间】:2020-03-07 16:18:09 【问题描述】:

下午好。我正在尝试将性能计数器从 Windows 机器读取到 CSV 文件。我能够成功地做到这一点。问题是,尽管我将编码设置为 UTF8,但我仍然无法在 CSV 结果中获得像“ç”或“õ”这样的字符。相反,当我使用 Excell 查看提取时,我得到 '??'字符代替他们的位置。

从网络相关问题中,我无法找到适合我的“问题”的解决方案。 在此先感谢各位。

#Define Input and output filepath

$servers=get-content ...
$outfile=...

################################################################################################################

#Function to have the customized output in CSV format
function Export-CsvFile 
[CmdletBinding(DefaultParameterSetName='Delimiter',
  SupportsShouldProcess=$true, ConfirmImpact='Medium')]
param(
[Parameter(Mandatory=$true, ValueFromPipeline=$true,
           ValueFromPipelineByPropertyName=$true)]
[System.Management.Automation.PSObject]
$InputObject,

[Parameter(Mandatory=$true, Position=0)]
[Alias('PSPath')]
[System.String]
$Path,

#region -Append 
[Switch]
$Append,
#endregion 

[Switch]
$Force,

[Switch]
$NoClobber,

[ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32','BigEndianUnicode','Default','OEM')]
[System.String]
$Encoding,

[Parameter(ParameterSetName='Delimiter', Position=1)]
[ValidateNotNull()]
[System.Char]
$Delimiter,

[Parameter(ParameterSetName='UseCulture')]
[Switch]
$UseCulture,

[Alias('NTI')]
[Switch]
$NoTypeInformation)

begin

# This variable will tell us whether we actually need to append
# to existing file
$AppendMode = $false

 try 
  $outBuffer = $null
  if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
  
      $PSBoundParameters['OutBuffer'] = 1
  
  $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Export-Csv',
    [System.Management.Automation.CommandTypes]::Cmdlet)


                #String variable to become the target command line
                $scriptCmdPipeline = ''

                # Add new parameter handling
                #region Dmitry: Process and remove the Append parameter if it is present
                if ($Append) 

                                $PSBoundParameters.Remove('Append') | Out-Null

  if ($Path) 
   if (Test-Path $Path)         
    # Need to construct new command line
    $AppendMode = $true

    if ($Encoding.Length -eq 0) 
     # ASCII is default encoding for Export-CSV
     #$Encoding = 'ASCII'
     $Encoding = 'UTF8'
    


    # For Append we use ConvertTo-CSV instead of Export
    $scriptCmdPipeline += 'ConvertTo-Csv -NoTypeInformation '

    # Inherit other CSV convertion parameters
    if ( $UseCulture ) 
     $scriptCmdPipeline += ' -UseCulture '
    
    if ( $Delimiter ) 
     $scriptCmdPipeline += " -Delimiter '$Delimiter' "
     

    # Skip the first line (the one with the property names) 
    $scriptCmdPipeline += ' | Foreach-Object $start=$true'
    $scriptCmdPipeline += 'if ($start) $start=$false else $_ '

    # Add file output
    $scriptCmdPipeline += " | Out-File -FilePath '$Path' -Encoding '$Encoding' -Append "

    if ($Force) 
     $scriptCmdPipeline += ' -Force'
    

    if ($NoClobber) 
     $scriptCmdPipeline += ' -NoClobber'
       
   
  
 



 $scriptCmd = & $wrappedCmd @PSBoundParameters 

 if ( $AppendMode ) 
  # redefine command line
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      $scriptCmdPipeline
    )
 else 
  # execute Export-CSV as we got it because
  # either -Append is missing or file does not exist
  $scriptCmd = $ExecutionContext.InvokeCommand.NewScriptBlock(
      [string]$scriptCmd
    )


# standard pipeline initialization
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)

  catch 
   throw




process

  try 
      $steppablePipeline.Process($_)
   catch 
      throw
  


end

  try 
      $steppablePipeline.End()
   catch 
      throw
  




################################################################################################################


#Actual script starts here 
#"Global": Function's Scope
function Global:Convert-HString       
[CmdletBinding()]            
 Param             
   (
    [Parameter(Mandatory=$false,
               ValueFromPipeline=$true,
               ValueFromPipelineByPropertyName=$true)]
    [String]$HString
   )#End Param

Begin 

    Write-Verbose "Converting Here-String to Array"
#Begin
Process 

    $HString -split "`n" | ForEach-Object 

        $ComputerName = $_.trim()
        if ($ComputerName -notmatch "#")
            
                $ComputerName
                


        
#Process
End 

    # Nothing to do here.
#End

#Convert-HString


#Performance counters declaration

function Get-CounterStats  
param 
    ( 
    [String]$ComputerName = $ENV:ComputerName

    ) 

$Object =@()

######################################List of the desired Counters#########################333

$CounterEN = @" 
Processor(_Total)\*
Memory\*
"@ 

        (Get-Counter -ComputerName $ComputerName -Counter (Convert-HString -HString $CounterEN)) |  
        ForEach-Object  
        $path = $_.path 
        New-Object PSObject -Property @
        computerName=$ComputerName
        Counter        = ($path  -split "\\")[-2,-1] -join "-" 
        Item        = $_.InstanceName 
        Value = [Math]::Round($_.CookedValue,2)
        datetime=(Get-Date -format "yyyy-MM-d hh:mm:ss")
         

        


 

#Collecting counter information for target servers

foreach($server in $Servers)

$d=Get-CounterStats -ComputerName $server |Select-Object computerName,Counter,Item,Value,datetime
$d |Export-CsvFile $outfile  -Append -NoTypeInformation -Encoding UTF8



#End of Script

【问题讨论】:

【参考方案1】:

它应该可以工作,除非您附加到文件开头没有 Utf8 BOM (EF BB BF) 的文件。

get-content file.csv -Encoding byte | select -first 3 | format-hex


           Path:

           00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F

00000000   EF BB BF                                         

【讨论】:

我能够通过以下示例导出到具有正确编码的 CSV:$CounterPT = "\Processor(*)*" $CounterPT |获取计数器 - 连续 | Export-CsvFile $outfile -Append -NoTypeInformation -encoding UTF8 因此这让我相信问题出在自定义CSV格式的代码中。 感谢您的帮助。这是导出并使用您的行后的结果: Path: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 00000000 22 63 6F "co 我会使用export-csv。或add-contentOut-file -append 不检查 BOM。【参考方案2】:

所以,我重做我的脚本以解决问题。 我正在将它提供给可能需要他用于未来工作的人。

#Get IP and Machine Name
$IPAddress = Get-WmiObject Win32_NetworkAdapterConfiguration | Where-Object $_.Ipaddress.length -gt 1 
$IPAddress = $IPAddress.ipaddress[0].ToString()
$IPAddress = $IPAddress -replace '\.', '_'

#Define output filepath
$TimeStamp=Get-Date -Format "#dd_MM_yyyy#HH_mm_ss"
$TimeStamp =$TimeStamp.ToString()
$TimeStamp = $TimeStamp -replace ':', '_'
$outfile="C:\" + $env:computername + '#'+  $IPAddress + $TimeStamp + "#CounterResults" + '.csv'
$outfileErrors="C:\" + $env:computername + '#'+  $IPAddress + $TimeStamp + "#CounterErrors" + '.txt'


################################################################################################################ 
################################################################################################################ 


#Actual script starts here  


$Counters = @(
    '\Memory\*'
)

try
            $ErrorActionPreference = "Stop"; #Make all errors terminating so that try/catch works

            Get-Counter -Counter $Counters -Continuous | ForEach 
                $_.CounterSamples | ForEach 
                    [pscustomobject]@
                        TimeStamp = $_.TimeStamp
                        ComputerName=[String]$ENV:ComputerName
                        Path = ($_.Path -split "\\")[-2,-1] -join "-" ;
                        Value = [Math]::Round($_.CookedValue,2) 
                    
                
             | Export-Csv -Path $outfile -Append -NoTypeInformation -Encoding UTF8





catch

 $_|Out-File $outfileErrors -Append

【讨论】:

以上是关于CSV 脚本的 PowerShell 获取计数器,编码问题的主要内容,如果未能解决你的问题,请参考以下文章

如何使用Powershell脚本获取Azure VM来宾指标(性能计数器)

powershell Powershell脚本获取OU的所有活动目录用户并生成计数

获取与 powershell 计数器一起使用的 cpu 百分比

输出 csv 文件时,Powershell 脚本在服务器上使用大量内存

Powershell - 针对大型目录运行脚本时出现内存不足错误

使用Powershell将csv导入SharePoint列表