Usar PowerShell para escribir un archivo en UTF-8 sin la lista de materiales
Out-File
parece forzar la lista de materiales cuando se usa UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
¿Cómo puedo escribir un archivo en UTF-8 sin BOM usando PowerShell?
Actualización 2021
PowerShell ha cambiado un poco desde que escribí esta pregunta hace 10 años. Consulte varias respuestas a continuación, ¡tienen mucha buena información!
Usar la clase de .NET UTF8Encoding
y pasarla $False
al constructor parece funcionar:
$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
La forma correcta a partir de ahora es utilizar una solución recomendada por @Roman Kuzmin en los comentarios a @M. Respuesta de Dudley :
[IO.File]::WriteAllLines($filename, $content)
(También lo acorté un poco eliminando System
aclaraciones innecesarias del espacio de nombres; se sustituirá automáticamente de forma predeterminada).
Pensé que esto no sería UTF, pero encontré una solución bastante simple que parece funcionar...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
Para mí, esto da como resultado un archivo utf-8 sin bom, independientemente del formato fuente.
Nota: Esta respuesta se aplica a Windows PowerShell ; por el contrario, en la edición multiplataforma PowerShell Core (v6+), UTF-8 sin BOM es la codificación predeterminada en todos los cmdlets.
En otras palabras: si está utilizando PowerShell [Core] versión 6 o superior , obtendrá archivos UTF-8 sin BOM de forma predeterminada (que también puede solicitar explícitamente con
-Encoding utf8
/-Encoding utf8NoBOM
, mientras que obtiene con codificación -BOM con-utf8BOM
).Si está ejecutando Windows 10 o superior y está dispuesto a cambiar a la codificación UTF-8 sin BOM en todo el sistema (lo que , sin embargo, tiene consecuencias de gran alcance ), incluso se puede hacer que Windows PowerShell use UTF sin BOM. 8 consistentemente : vea esta respuesta .
Para complementar la respuesta simple y pragmática de M. Dudley (y la reformulación más concisa de ForNeVer ):
Una alternativa simple y nativa de PowerShell (sin transmisión) es usar
New-Item
, que (curiosamente) crea archivos UTF-8 sin BOM de forma predeterminada incluso en Windows PowerShell:# Note the use of -Raw to read the file as a whole. # Unlike with Set-Content / Out-File *no* trailing newline is appended. $null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
Nota: Para guardar el resultado de comandos arbitrarios en el mismo formato que
Out-File
lo haría, canalice aOut-String
primero; p.ej:$null = New-Item -Force Out.txt -Value (Get-ChildItem | Out-String)
Para mayor comodidad, a continuación se muestra la función personalizada
Out-FileUtf8NoBom
Out-File
avanzada , una alternativa basada en canalizaciones que imita , lo que significa:- puedes usarlo como
Out-File
en una tubería. - Los objetos de entrada que no son cadenas tienen el formato que tendrían si los enviara a la consola, tal como con
Out-File
. - un
-UseLF
modificador adicional le permite utilizar líneas nuevas de formato LF únicamente ("`n"
) en formato Unix en lugar de las líneas nuevas CRLF ("`r`n"
) de formato Windows que normalmente obtiene.
- puedes usarlo como
Ejemplo:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath # Add -UseLF for Unix newlines
Tenga en cuenta cómo (Get-Content $MyPath)
se incluye entre (...)
, lo que garantiza que todo el archivo se abra, se lea en su totalidad y se cierre antes de enviar el resultado a través de la canalización. Esto es necesario para poder volver a escribir en el mismo archivo (actualizarlo en su lugar ).
Sin embargo, generalmente esta técnica no es recomendable por 2 razones: (a) todo el archivo debe caber en la memoria y (b) si se interrumpe el comando, se perderán datos.
Una nota sobre el uso de la memoria :
- La propia respuesta de M. Dudley
y la
New-Item
alternativa anterior requieren que todo el contenido del archivo se acumule primero en la memoria , lo que puede resultar problemático con conjuntos de entrada grandes. - La siguiente función no requiere esto, porque se implementa como una función proxy (contenedor) (para obtener un resumen conciso de cómo definir dichas funciones, consulte esta respuesta ).
Código fuente de la funciónOut-FileUtf8NoBom
:
Nota: La función también está disponible como Gist con licencia del MIT y en el futuro solo se mantendrá este último.
Puedes instalarlo directamente con el siguiente comando (aunque personalmente puedo asegurarte que hacerlo es seguro, siempre debes verificar el contenido de un script antes de ejecutarlo directamente de esta manera):
# Download and define the function.
irm https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be/raw/Out-FileUtf8NoBom.ps1 | iex
function Out-FileUtf8NoBom {
<#
.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-File parameters:
* Only a literal output path is supported, and only as a parameter.
* -Force is not supported.
* Conversely, an extra -UseLF switch is supported for using LF-only newlines.
.NOTES
The raison d'être for this advanced function is that Windows PowerShell
lacks the ability to write UTF-8 files without a BOM: using -Encoding UTF8
invariably prepends a BOM.
Copyright (c) 2017, 2022 Michael Klement <[email protected]> (http://same2u.net),
released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).
#>
[CmdletBinding(PositionalBinding=$false)]
param(
[Parameter(Mandatory, Position = 0)] [string] $LiteralPath,
[switch] $Append,
[switch] $NoClobber,
[AllowNull()] [int] $Width,
[switch] $UseLF,
[Parameter(ValueFromPipeline)] $InputObject
)
begin {
# Convert the input path to a full one, since .NET's working dir. usually
# differs from PowerShell's.
$dir = Split-Path -LiteralPath $LiteralPath
if ($dir) { $dir = Convert-Path -ErrorAction Stop -LiteralPath $dir } else { $dir = $pwd.ProviderPath }
$LiteralPath = [IO.Path]::Combine($dir, [IO.Path]::GetFileName($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 System.IO.StreamWriter $LiteralPath, $Append
$htOutStringArgs = @{}
if ($Width) { $htOutStringArgs += @{ Width = $Width } }
try {
# Create the script block with the command to use in the steppable pipeline.
$scriptCmd = {
& Microsoft.PowerShell.Utility\Out-String -Stream @htOutStringArgs |
. { process { if ($UseLF) { $sw.Write(($_ + "`n")) } else { $sw.WriteLine($_) } } }
}
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
catch { throw }
}
process
{
$steppablePipeline.Process($_)
}
end {
$steppablePipeline.End()
$sw.Dispose()
}
}
A partir de la versión 6 , PowerShell admite la UTF8NoBOM
codificación tanto para el contenido del conjunto como para el archivo de salida e incluso la utiliza como codificación predeterminada.
Así que en el ejemplo anterior debería ser simplemente así:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath