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?
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?
Respuestas:
Usar la UTF8Encoding
clase de .NET y pasar $False
al constructor parece funcionar:
$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
[System.IO.File]::WriteAllLines($MyPath, $MyFile)
es suficiente. Esta WriteAllLines
sobrecarga escribe exactamente UTF8 sin BOM.
WriteAllLines
parece requerir $MyPath
ser absoluto.
WriteAllLines
obtiene el directorio actual de [System.Environment]::CurrentDirectory
. Si abre PowerShell y luego cambia su directorio actual (usando cd
o Set-Location
), [System.Environment]::CurrentDirectory
no se cambiará y el archivo terminará en el directorio incorrecto. Puedes evitar esto por [System.Environment]::CurrentDirectory = (Get-Location).Path
.
La forma correcta a partir de ahora es utilizar una solución recomendada por @Roman Kuzmin en los comentarios a @M. Dudley responde :
[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).
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
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 da como resultado un archivo utf-8 sin bom independientemente del formato de origen.
-encoding utf8
cumplir con mis requisitos.
-Encoding ASCII
evita el problema de la lista de materiales, pero obviamente solo obtienes caracteres ASCII de 7 bits . Dado que ASCII es un subconjunto de UTF-8, el archivo resultante es técnicamente también un archivo UTF-8 válido, pero todos los caracteres no ASCII en su entrada se convertirán en ?
caracteres literales .
-encoding utf8
aún saca UTF-8 con una lista de materiales. :(
Nota: esta respuesta se aplica a Windows PowerShell ; por el contrario, en la edición multiplataforma de 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 la codificación -BOM con -utf8BOM
).
Para complementar la propia respuesta simple y pragmática de M. Dudley (y la reformulación más concisa de ForNeVeR ):
Por conveniencia, aquí está la función avanzada Out-FileUtf8NoBom
, una alternativa basada en canalización que imitaOut-File
, lo que significa:
Out-File
en una tubería.Out-File
.Ejemplo:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
Tenga en cuenta cómo (Get-Content $MyPath)
se incluye (...)
, lo que garantiza que todo el archivo se abra, se lea por completo 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 ).
En general, sin embargo, esta técnica no es aconsejable 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 :
Código fuente deOut-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.ProviderPath) # Caveat: Older .NET Core versions don'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()
}
}
A partir de la versión 6, powershell admite la UTF8NoBOM
codificación tanto para el contenido del conjunto como para el archivo externo e incluso lo utiliza como codificación predeterminada.
Entonces, en el ejemplo anterior, simplemente debería ser así:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
$PSVersionTable.PSVersion
Cuando se usa en Set-Content
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 [IO.File]::WriteAllLines()
o similar es que debería funcionar bien con cualquier tipo de elemento y ruta, no solo rutas de archivos reales.
Este script convertirá, a UTF-8 sin BOM, todos los archivos .txt en DIRECTORIO1 y los enviará a DIRECTORIO2
foreach ($i in ls -name DIRECTORY1\*.txt)
{
$file_content = Get-Content "DIRECTORY1\$i";
[System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}
[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"
}
Fuente Cómo eliminar la marca de orden de bytes UTF8 (BOM) de un archivo usando PowerShell
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 | Out-String |
para agregar CRLF 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 a un archivo en UTF-8 sin BOM
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
es Convert-Path $MyPath
; si desea garantizar un CRLF final, simplemente use [System.IO.File]::WriteAllLines()
incluso con una sola cadena de entrada (no es necesario Out-String
).
Una técnica que utilizo es redirigir la salida a un archivo ASCII usando el cmdlet Out-File .
Por ejemplo, a menudo ejecuto scripts SQL que crean otro script SQL para ejecutar en Oracle. Con la redirección simple (">"), la salida estará en UTF-16, lo cual no es reconocido por SQLPlus. Para evitar 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 de SQLPlus sin preocupaciones de Unicode:
sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
-Encoding ASCII
evita el problema de la lista de materiales, pero obviamente solo obtienes soporte para caracteres ASCII de 7 bits . Dado que ASCII es un subconjunto de UTF-8, el archivo resultante es técnicamente también un archivo UTF-8 válido, pero todos los caracteres no ASCII en su entrada se convertirán en ?
caracteres literales .
Cambie 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)
}
Por alguna razón, las WriteAllLines
llamadas seguían produciendo una lista de materiales para mí, con el UTF8Encoding
argumento BOMless 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 que la ruta del archivo sea absoluta para que funcione. De lo contrario, escribió el archivo en mi escritorio. Además, supongo que esto solo funciona si sabe que su BOM es de 3 bytes. No tengo idea de cuán confiable es esperar un formato / longitud BOM determinado basado en la codificación.
Además, como está escrito, esto probablemente solo funcione si su archivo se ajusta a una matriz de PowerShell, que parece tener un límite de longitud de algún valor menor que [int32]::MaxValue
en mi máquina.
WriteAllLines
sin un argumento de codificación nunca escribe una lista de materiales en sí , pero es concebible que su cadena comience con el carácter BOM ( U+FEFF
), que al escribir efectivamente creó una lista de materiales UTF-8; por ejemplo: $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(omita el [char] 0xfeff +
para ver que no se escribe BOM).
[Environment]::CurrentDirectory = $PWD.ProviderPath
, o, como una alternativa más genérica a su "$(pwd)\..."
enfoque (mejor:, "$pwd\..."
incluso mejor: "$($pwd.ProviderPath)\..."
o (Join-Path $pwd.ProviderPath ...)
), use(Convert-Path BOMthetorpedoes.txt)
U+FEFF
abstracto .
Podría usar a continuación para obtener UTF8 sin BOM
$MyFile | Out-File -Encoding ASCII
ASCII
no es UTF-8, pero tampoco es la página de códigos ANSI actual, estás pensando Default
; ASCII
verdaderamente es una codificación ASCII de 7 bits, con puntos de código> = 128 convertidos en ?
instancias literales .
-Encoding ASCII
hecho, solo sea ASCII de 7 bits: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- ä
se ha transcrito a ?
. Por el contrario, -Encoding Default
("ANSI") lo preservaría correctamente.
Este funciona para mí (use "Predeterminado" en lugar de "UTF8"):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
El resultado es ASCII sin BOM.
Default
codificación, se utilizará la página de códigos ANSI actual del sistema, que no es UTF-8, como lo solicité.