Skip to content

Instantly share code, notes, and snippets.

@exactmike
Created December 11, 2018 19:18
Show Gist options
  • Select an option

  • Save exactmike/866f5cabfdb7663a31a75381f650bf64 to your computer and use it in GitHub Desktop.

Select an option

Save exactmike/866f5cabfdb7663a31a75381f650bf64 to your computer and use it in GitHub Desktop.
PowerShell function to output a file in UTF8 encoding with no BOM
function Out-FileUtf8NoBom
{
#requires -version 3
<#
.SYNOPSIS
Outputs to a UTF-8-encoded file *without a BOM* (byte-order mark).
.DESCRIPTION
Mimics the most important aspects of Out-File:
* Input objects are sent to Out-String first.
* -Append allows you to append to an existing file, -NoClobber prevents
overwriting of an existing file.
* -Width allows you to specify the line width for the text representations
of input objects that aren't strings.
However, it is not a complete implementation of all Out-String parameters:
* Only a literal output path is supported, and only as a parameter.
* -Force is not supported.
Caveat: *All* pipeline input is buffered before writing output starts,
but the string representations are generated and written to the target
file one by one.
.NOTES
The raison d'être for this advanced function is that, as of PowerShell v5,
Out-File still lacks the ability to write UTF-8 files without a BOM:
using -Encoding UTF8 invariably prepends a BOM.
http://stackoverflow.com/questions/5596982/using-powershell-to-write-a-file-in-utf-8-without-the-bom
#>
[CmdletBinding()]
param
(
[Parameter(Mandatory, Position = 0)]
[string] $LiteralPath
,
[switch] $Append
,
[switch] $NoClobber
,
[AllowNull()] [int] $Width
,
[Parameter(ValueFromPipeline)]
$InputObject
)
# Make sure that the .NET framework sees the same working dir. as PS
# and resolve the input path to a full path.
#[Environment]::CurrentDirectory = $PWD
$LiteralPath = [IO.Path]::GetFullPath($LiteralPath)
# If -NoClobber was specified, throw an exception if the target file already
# exists.
if ($NoClobber -and (Test-Path $LiteralPath))
{
Throw [IO.IOException] "The file '$LiteralPath' already exists."
}
# Create a StreamWriter object.
# Note that we take advantage of the fact that the StreamWriter class by default:
# - uses UTF-8 encoding
# - without a BOM.
$sw = New-Object IO.StreamWriter $LiteralPath, $Append
$htOutStringArgs = @{}
if ($Width)
{
$htOutStringArgs += @{ Width = $Width }
}
# Note: By not using begin / process / end blocks, we're effectively running
# in the end block, which means that all pipeline input has already
# been collected in automatic variable $Input.
# We must use this approach, because using | Out-String individually
# in each iteration of a process block would format each input object
# with an indvidual header.
try
{
$InputObject | Out-String -Stream @htOutStringArgs | ForEach-Object { $sw.WriteLine($_) }
}
finally
{
$sw.Dispose()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment