Uso de 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?

Author: M. Dudley, 2011-04-08

14 answers

Usando. NET UTF8Encoding la clase y pasar $False al constructor parece funcionar:

$MyFile = Get-Content $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)
 174
Author: M. Dudley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-15 23:56:32

La forma apropiada a partir de ahora es usar una solución recomendada por @ Roman Kuzmin en comentarios a @M. Dudley respuesta :

[IO.File]::WriteAllLines($filename, $content)

(También lo he acortado un poco eliminando la aclaración innecesaria del espacio de nombres System - se sustituirá automáticamente por defecto.)

 59
Author: ForNeVeR,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 12:34:44

Pensé que esto no sería UTF, pero acabo de encontrar una solución bastante simple que parece funcionar...

Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext

Para mí esto resulta en un utf-8 sin archivo bom independientemente del formato de origen.

 32
Author: Lenny,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-02 00:26:54

Para complementar la propia respuesta simple y pragmática de M. Dudley (y la reformulación más concisa de ForNeVeR):

Para mayor comodidad, aquí está la función avanzada Out-FileUtf8NoBom, una alternativa basada en pipeline que imita Out-File, lo que significa:

  • puede usarlo igual que Out-File en una canalización.
  • los objetos de entrada que no son cadenas tienen el formato que tendrían si los enviaras a la consola, al igual que con Out-File.

Ejemplo:

(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath

Tenga en cuenta cómo (Get-Content $MyPath) está encerrado en (...), lo que garantiza que todo el archivo se abra, lea en su totalidad y se cierre antes de enviar el resultado a través de la canalización. Esto es necesario para poder escribir de nuevo en el mismo archivo (actualizarlo en su lugar).
En general, sin embargo, 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, los datos serán perder.

Una nota sobre el uso de la memoria :

  • La propia respuesta de M. Dudley requiere que todo el contenido del archivo se construya en memoria primero, lo que puede ser problemático con archivos grandes.
  • La función a continuación mejora esto solo ligeramente: todos los objetos de entrada todavía se almacenan en búfer primero, pero sus representaciones de cadena se generan y escriben en el archivo de salida uno por uno.

Código fuente de Out-FileUtf8NoBom (también disponible como Gist con licencia MIT):

<#
.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.

#>
function Out-FileUtf8NoBom {

  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)] [string] $LiteralPath,
    [switch] $Append,
    [switch] $NoClobber,
    [AllowNull()] [int] $Width,
    [Parameter(ValueFromPipeline)] $InputObject
  )

  #requires -version 3

  # Make sure that the .NET framework sees the same working dir. as PS
  # and resolve the input path to a full path.
  [System.IO.Directory]::SetCurrentDirectory($PWD) # Caveat: .NET Core doesn't support [Environment]::CurrentDirectory
  $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 {
    $Input | Out-String -Stream @htOutStringArgs | % { $sw.WriteLine($_) }
  } finally {
    $sw.Dispose()
  }

}
 22
Author: mklement0,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-24 22:26:37

Este script convertirá, a UTF-8 sin BOM, todo .archivos txt en DIRECTORY1 y enviarlos a DIRECTORY2

foreach ($i in ls -name DIRECTORY1\*.txt)
{
    $file_content = Get-Content "DIRECTORY1\$i";
    [System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}
 4
Author: jamhan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-05-01 05:22:46

Al usar Set-Content en lugar de Out-File, puede especificar la codificación Byte, que se puede usar para escribir una matriz de bytes en un archivo. Esto en combinación con una codificación UTF8 personalizada que no emite la lista de materiales da el resultado deseado:

# This variable can be reused
$utf8 = New-Object System.Text.UTF8Encoding $false

$MyFile = Get-Content $MyPath -Raw
Set-Content -Value $utf8.GetBytes($MyFile) -Encoding Byte -Path $MyPath

La diferencia con el uso de [IO.File]::WriteAllLines() o similar es que debería funcionar bien con cualquier tipo de elemento y ruta, no solo rutas de archivo reales.

 3
Author: Lucero,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-23 17:48:31

Tenía el mismo problema. Eso hizo el truco para mí:

$MyFile | Out-File -Encoding Oem $MyPath

Al abrir el archivo con Visual Studio Code o Notepad++ se muestra como UTF-8

 2
Author: delianmc,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-07-19 18:51:53

Una técnica que utilizo es redirigir la salida a un archivo ASCII utilizando el cmdlet Out-File.

Por ejemplo, a menudo corro scripts SQL que crean otro script SQL para ejecutar en Oracle. Con la redirección simple ( " > " ), la salida estará en UTF-16 que no es reconocida por SQLPlus. Para trabajar alrededor de esto:

sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force

El script generado se puede ejecutar a través de otra sesión SQLPlus sin preocupaciones Unicode:

sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
 1
Author: Erik Anderson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-09-22 19:36:20

Cambiar varios archivos por extensión a UTF-8 sin BOM:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach($i in ls -recurse -filter "*.java") {
    $MyFile = Get-Content $i.fullname 
    [System.IO.File]::WriteAllLines($i.fullname, $MyFile, $Utf8NoBomEncoding)
}
 0
Author: Jaume Suñer Mut,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-10-03 13:59:08

Por cualquier razón, las llamadas WriteAllLines todavía estaban produciendo un BOM para mí, con el argumento BOMless UTF8Encoding y sin él. Pero lo siguiente funcionó para mí:

$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])

Tuve que hacer la ruta del archivo absoluta para que funcionara. De lo contrario escribió el archivo a mi escritorio. Además, supongo que esto solo funciona si sabes que tu BOM es de 3 bytes. No tengo idea de lo confiable que es esperar un formato/longitud de BOM determinado basado en la codificación.

También, tal como está escrito, esto probablemente solo funcione si su archivo encaja en una matriz de powershell, que parece tener un límite de longitud de algún valor menor que [int32]::MaxValue en mi máquina.

 0
Author: xdhmoore,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-01 06:28:43
    [System.IO.FileInfo] $file = Get-Item -Path $FilePath 
    $sequenceBOM = New-Object System.Byte[] 3 
    $reader = $file.OpenRead() 
    $bytesRead = $reader.Read($sequenceBOM, 0, 3) 
    $reader.Dispose() 
    #A UTF-8+BOM string will start with the three following bytes. Hex: 0xEF0xBB0xBF, Decimal: 239 187 191 
    if ($bytesRead -eq 3 -and $sequenceBOM[0] -eq 239 -and $sequenceBOM[1] -eq 187 -and $sequenceBOM[2] -eq 191) 
    { 
        $utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False) 
        [System.IO.File]::WriteAllLines($FilePath, (Get-Content $FilePath), $utf8NoBomEncoding) 
        Write-Host "Remove UTF-8 BOM successfully" 
    } 
    Else 
    { 
        Write-Warning "Not UTF-8 BOM file" 
    }  

Source Cómo eliminar la marca de orden de bytes UTF8 (BOM) de un archivo usando PowerShell

 0
Author: frank tan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-02-08 05:47:40

Si desea usar [System.IO.File]::WriteAllLines(), debe convertir el segundo parámetro a String[] (si el tipo de $MyFile es Object[]), y también especificar la ruta absoluta con $ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath), como:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Set-Variable MyFile
[System.IO.File]::WriteAllLines($ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath), [String[]]$MyFile, $Utf8NoBomEncoding)

Si desea usar [System.IO.File]::WriteAllText(), a veces debe canalizar el segundo parámetro en | Out-String | para agregar CRLFs al final de cada línea explícitamente (Especialmente cuando los usa con ConvertTo-Csv):

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Out-String | Set-Variable tmp
[System.IO.File]::WriteAllText("/absolute/path/to/foobar.csv", $tmp, $Utf8NoBomEncoding)

O puedes usar [Text.Encoding]::UTF8.GetBytes() con Set-Content -Encoding Byte:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
Get-ChildItem | ConvertTo-Csv | Out-String | % { [Text.Encoding]::UTF8.GetBytes($_) } | Set-Content -Encoding Byte -Path "/absolute/path/to/foobar.csv"

Ver: Cómo escribir el resultado de ConvertTo-Csv en un archivo en UTF-8 sin BOM

 0
Author: satob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-04-24 13:42:19

Podría utilizar a continuación para obtener UTF8 sin BOM

$MyFile | Out-File -Encoding ASCII
 -2
Author: Robin Wang,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-09-22 20:43:38

Este funciona para mí (use "Default" en lugar de "UTF8"):

$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath

El resultado es ASCII sin BOM.

 -3
Author: Krzysztof,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-05-06 12:34:44