使用Export-CSV

时间:2018-04-24 10:21:35

标签: powershell csv utf-8 export-to-csv byte-order-mark

是否有一种干净的方法可以将SEP=,附加到由Export-CSV创建的CSV文件的开头?

注意:这是一个XY问题;请参阅为什么这需要以获取有关我的root问题的信息。我要求SEP部分,因为这似乎是最好的修复,因为我需要继续使用UTF8 CSV并且不希望用户必须改变它们的工作方式以避免问题。

创建后修改文件

简单的选择就是这个;但是它感觉很乱(即我们释放文件上的锁然后必须返回并更新它)。

function Repair-Csv {
    [CmdletBinding()]
    Param (
        [Parameter(Mandatory = $true, ValueFromPipeline = $true)]
        [string]$Path
    )
    Begin {
        $sep = "SEP=,`r`n" 
    }
    Process {
        $sep + (Get-Content -Path $Path -Raw) | Set-Content -Path $Path
    }
}

代理功能解决方案尝试

我尝试创建代理函数(下面的代码),但发现包装的Export-CSV命令没有将其输出附加到我调整的文件,而是读取我已编写的内容并尝试使用SEP=作为列标题;所以我最终没有数据(除非我的导出对象的某个属性恰好被称为SEP=

# $MetaData = New-Object System.Management.Automation.CommandMetaData (Get-Command 'Export-CSV')
# [System.Management.Automation.ProxyCommand]::Create($MetaData)
function Export-CsvAdvanced {
    [CmdletBinding(DefaultParameterSetName='Delimiter', SupportsShouldProcess=$true, ConfirmImpact='Medium', HelpUri='http://go.microsoft.com/fwlink/?LinkID=113299')]
    param (
        [Parameter(Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)]
        [psobject]
        ${InputObject}
        ,
        [Parameter(Position=0)]
        [ValidateNotNullOrEmpty()]
        [string]
        ${Path}
        ,
        [Alias('PSPath')]
        [ValidateNotNullOrEmpty()]
        [string]
        ${LiteralPath}
        ,
        [switch]
        ${Force}
        ,
        [Alias('NoOverwrite')]
        [switch]
        ${NoClobber}
        ,
        [ValidateSet('Unicode','UTF7','UTF8','ASCII','UTF32','BigEndianUnicode','Default','OEM')]
        [string]
        ${Encoding}
        ,
        [switch]
        ${Append}
        ,
        [Parameter(ParameterSetName='Delimiter', Position=1)]
        [ValidateNotNull()]
        [char]
        ${Delimiter}
        ,
        [Parameter(ParameterSetName='UseCulture')]
        [switch]
        ${UseCulture}
        ,
        [Alias('NTI')]
        [switch]
        ${NoTypeInformation}
        ,
        [Alias('SEP')]
        [switch]
        ${PrefixWithSep}
    )
    begin {
        try {
            $outBuffer = $null
            if ($PSBoundParameters.TryGetValue('OutBuffer', [ref]$outBuffer))
            {
                $PSBoundParameters['OutBuffer'] = 1
            }
            if ($PrefixWithSep.IsPresent) {
                if (!$Delimiter) {
                    if ($UseCulture.IsPresent) {
                        $Delimiter = [System.Globalization.CultureInfo]::CurrentCulture.TextInfo.ListSeparator
                    } else {
                        $Delimiter = ','
                    }
                }
                $splat = @{}
                if ($Encoding) { $splat.Add('Encoding', $Encoding) }
                if ($Force) { $splat.Add('Force', $Force) }
                if ($Append) { $splat.Add('Append', $Append) }
                if ($NoClobber) { $splat.Add('NoClobber', $NoClobber) }
                if ($PSCmdlet.ParameterSetName -eq 'PSPath') {
                    $splat.Add('LiteralPath', $LiteralPath)
                } else {
                    $splat.Add('FilePath', $Path)
                }
                ("SEP={0}`r`n" -f $Delimiter) | Out-File @splat
                $PSBoundParameters.Remove('PrefixWithSep') #Export-CSV won't understand our custom parameter, so remove it (NB: if this is not the first call this will already have been removed)
                $PSBoundParameters['Append'] = $true #we don't want to delete the file after adding a header
                $PSBoundParameters['Force'] = $true #Don't treat `SEP=,` as a heading when appending to the file
            }

            $wrappedCmd = $ExecutionContext.InvokeCommand.GetCommand('Microsoft.PowerShell.Utility\Export-Csv', [System.Management.Automation.CommandTypes]::Cmdlet)
            $scriptCmd = {& $wrappedCmd @PSBoundParameters }
            $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
            $steppablePipeline.Begin($PSCmdlet)
        } catch {
            throw
        }
    }
    process {
        try {
            $steppablePipeline.Process($_)
        } catch {
            throw
        }
    }
    end {
        try {
            $steppablePipeline.End()
        } catch {
            throw
        }
    }
    <#
    .ForwardHelpTargetName Microsoft.PowerShell.Utility\Export-Csv
    .ForwardHelpCategory Cmdlet
    #>
}

#test populated
Get-Process | Export-CsvAdvanced -Path 'c:\temp\processSep.csv' -NoTypeInformation -PrefixWithSep
Get-Process | Export-CsvAdvanced -Path 'c:\temp\process.csv' -NoTypeInformation
#test blank
$blankArray = @()
$blankArray | Export-CsvAdvanced -Path 'c:\temp\emptySep.csv' -NoTypeInformation -PrefixWithSep
$blankArray | Export-CsvAdvanced -Path 'c:\temp\empty.csv' -NoTypeInformation

我总是可以在ConvertTo-CSV上创建代理功能,添加流以将结果输出到文件;但这并不舒服(例如,如果有一个例外情况,那么试图正确处理这些流会变得很复杂。)

为什么需要这个?

我不严格需要SEP前缀;它会很有用;但事实是我正试图解决另一个问题(所以这是一个XY problem)。

我有一些将数据输出到CSV的功能。 在某些情况下,导出的文件是空白的(即没有结果)。在这种情况下,我仍然希望创建一个文件(即很明显代码已经运行并且没有结果,而代码根本没有运行)。 但是,我的CSV是UTF8,当它在Excel中显示为BOM时(即单元格A1包含)。

由于我必须继续使用CSV并且理想情况下希望继续使用UTF8,所以最好的解决方案是添加一些内容; one suggestionSEP=,指令的前缀(与其他国家/地区的用户共享CSV时也很有用,其中使用了不同的默认分隔符。)

0 个答案:

没有答案