Usar PowerShell para escribir un archivo en UTF-8 sin la lista de materiales

Resuelto sourcenouveau asked hace 13 años • 20 respuestas

Out-Fileparece 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!

sourcenouveau avatar Apr 08 '11 22:04 sourcenouveau
Aceptado

Usar la clase de .NET UTF8Encodingy pasarla $Falseal constructor parece funcionar:

$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
sourcenouveau avatar Apr 08 '2011 15:04 sourcenouveau

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 Systemaclaraciones innecesarias del espacio de nombres; se sustituirá automáticamente de forma predeterminada).

ForNeVeR avatar Oct 05 '2015 15:10 ForNeVeR

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.

Lenny avatar Dec 02 '2016 00:12 Lenny

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-Filelo haría, canalice a Out-Stringprimero; p.ej:

       $null = New-Item -Force Out.txt -Value (Get-ChildItem | Out-String) 
      
  • Para mayor comodidad, a continuación se muestra la función personalizadaOut-FileUtf8NoBomOut-File avanzada , una alternativa basada en canalizaciones que imita , lo que significa:

    • puedes usarlo como Out-Fileen 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 -UseLFmodificador 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.

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-Itemalternativa 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()
  }

}
mklement0 avatar Jan 23 '2016 21:01 mklement0

A partir de la versión 6 , PowerShell admite la UTF8NoBOMcodificació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
sc911 avatar Mar 09 '2019 12:03 sc911