Out-File
parece forçar a lista técnica ao usar UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Como gravar um arquivo em UTF-8 sem BOM usando o PowerShell?
Out-File
parece forçar a lista técnica ao usar UTF-8:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
Como gravar um arquivo em UTF-8 sem BOM usando o PowerShell?
Respostas:
Usar a UTF8Encoding
classe do .NET e passar $False
para o construtor 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)
é suficiente. Essa WriteAllLines
sobrecarga grava exatamente UTF8 sem BOM.
WriteAllLines
parece exigir $MyPath
ser absoluto.
WriteAllLines
obtém o diretório atual de [System.Environment]::CurrentDirectory
. Se você abrir o PowerShell e alterar o diretório atual (usando cd
ou Set-Location
), [System.Environment]::CurrentDirectory
não será alterado e o arquivo acabará no diretório errado. Você pode contornar isso por [System.Environment]::CurrentDirectory = (Get-Location).Path
.
A maneira correta a partir de agora é usar uma solução recomendada por @Roman Kuzmin nos comentários para @M. Dudley responde :
[IO.File]::WriteAllLines($filename, $content)
(Também reduzi um pouco removendo os System
esclarecimentos desnecessários do espaço para nome - ele será substituído automaticamente por padrão.)
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
Achei que isso não seria UTF, mas acabei de encontrar uma solução bastante simples que parece funcionar ...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
Para mim, isso resulta em um utf-8 sem arquivo bom, independentemente do formato de origem.
-encoding utf8
para minha exigência.
-Encoding ASCII
evita o problema da lista técnica, mas obviamente você recebe apenas caracteres ASCII de 7 bits . Como o ASCII é um subconjunto do UTF-8, o arquivo resultante também é tecnicamente um arquivo UTF-8 válido, mas todos os caracteres não ASCII da sua entrada serão convertidos em ?
caracteres literais .
-encoding utf8
ainda gera UTF-8 com uma BOM. :(
Nota: Esta resposta se aplica ao Windows PowerShell ; por outro lado, na edição do PowerShell Core para várias plataformas (v6 +), UTF-8 sem BOM é a codificação padrão em todos os cmdlets.
Em outras palavras: se você estiver usando o PowerShell [Core] versão 6 ou superior , por padrão , você obtém arquivos UTF-8 sem BOM (que você também pode solicitar explicitamente com -Encoding utf8
/ -Encoding utf8NoBOM
, enquanto que com a codificação -BOM -utf8BOM
).
Para complementar a resposta simples e pragmática de M. Dudley (e a reformulação mais concisa do ForNeVeR ):
Por conveniência, aqui está a função avançada Out-FileUtf8NoBom
, uma alternativa baseada em pipeline que imitaOut-File
, o que significa:
Out-File
em um pipeline.Out-File
.Exemplo:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
Observe como (Get-Content $MyPath)
está incluído (...)
, o que garante que o arquivo inteiro seja aberto, lido na íntegra e fechado antes de enviar o resultado pelo pipeline. Isso é necessário para poder gravar novamente no mesmo arquivo (atualize-o no local ).
Geralmente, porém, essa técnica não é aconselhável por 2 motivos: (a) o arquivo inteiro deve caber na memória e (b) se o comando for interrompido, os dados serão perdidos.
Uma observação sobre o uso da memória :
Código fonte deOut-FileUtf8NoBom
(também disponível como um Gist licenciado pelo 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 da versão 6, o powershell suporta a UTF8NoBOM
codificação para conteúdo definido e arquivo externo e até a usa como codificação padrão.
Portanto, no exemplo acima, deve ser simplesmente assim:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
$PSVersionTable.PSVersion
Ao usar em Set-Content
vez de Out-File
, você pode especificar a codificação Byte
, que pode ser usada para gravar uma matriz de bytes em um arquivo. Isso em combinação com uma codificação UTF8 personalizada que não emite a lista técnica fornece o resultado desejado:
# 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
A diferença de usar [IO.File]::WriteAllLines()
ou semelhante é que ele deve funcionar bem com qualquer tipo de item e caminho, não apenas os caminhos reais do arquivo.
Esse script converterá, para UTF-8 sem BOM, todos os arquivos .txt no DIRECTORY1 e os produzirá em DIRECTORY2
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"
}
Origem Como remover UTF8 Byte Order Mark (BOM) de um arquivo usando o PowerShell
Se você deseja usar [System.IO.File]::WriteAllLines()
, deve converter o segundo parâmetro para String[]
(se o tipo de $MyFile
for Object[]
) e também especificar o caminho absoluto com $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)
Se você deseja usar [System.IO.File]::WriteAllText()
, algumas vezes você deve canalizar o segundo parâmetro | Out-String |
para adicionar CRLFs ao final de cada linha explicitamente (especialmente quando você os usa 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)
Ou você pode usar [Text.Encoding]::UTF8.GetBytes()
com 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"
consulte: Como gravar o resultado do ConvertTo-Csv em um arquivo no UTF-8 sem BOM
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
é Convert-Path $MyPath
; se você deseja garantir um CRLF à direita, basta usar [System.IO.File]::WriteAllLines()
mesmo com uma única sequência de entrada (não é necessário Out-String
).
Uma técnica que utilizo é redirecionar a saída para um arquivo ASCII usando o cmdlet Out-File .
Por exemplo, geralmente executo scripts SQL que criam outro script SQL para executar no Oracle. Com o redirecionamento simples (">"), a saída será em UTF-16, que não é reconhecida pelo SQLPlus. Para contornar isso:
sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force
O script gerado pode ser executado através de outra sessão do SQLPlus sem preocupações com o Unicode:
sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
-Encoding ASCII
evita o problema da lista técnica, mas obviamente você só obtém suporte para caracteres ASCII de 7 bits . Como o ASCII é um subconjunto do UTF-8, o arquivo resultante também é tecnicamente um arquivo UTF-8 válido, mas todos os caracteres não ASCII da sua entrada serão convertidos em ?
caracteres literais .
Altere vários arquivos por extensão para UTF-8 sem 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 qualquer motivo, as WriteAllLines
chamadas ainda estavam produzindo uma lista técnica para mim, com o UTF8Encoding
argumento BOMless e sem ele. Mas o seguinte funcionou para mim:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
Eu tive que tornar o caminho do arquivo absoluto para que ele funcionasse. Caso contrário, ele gravou o arquivo na minha área de trabalho. Além disso, suponho que isso funcione apenas se você souber que sua lista técnica é de 3 bytes. Não tenho idéia de quão confiável é esperar um determinado formato / comprimento de lista técnica com base na codificação.
Além disso, conforme escrito, isso provavelmente só funcionará se o arquivo se encaixar em uma matriz do PowerShell, que parece ter um limite de tamanho de algum valor menor do que [int32]::MaxValue
na minha máquina.
WriteAllLines
sem um argumento de codificação nunca grava uma BOM propriamente dita , mas é concebível que sua string tenha começado com o caractere BOM ( U+FEFF
), que ao escrever efetivamente criou uma BOM UTF-8; por exemplo: $s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(omita o [char] 0xfeff +
para ver que nenhuma lista técnica é gravada).
[Environment]::CurrentDirectory = $PWD.ProviderPath
, ou, como uma alternativa mais genérica à sua "$(pwd)\..."
abordagem (melhor: "$pwd\..."
, ainda melhor: "$($pwd.ProviderPath)\..."
ou (Join-Path $pwd.ProviderPath ...)
), o uso(Convert-Path BOMthetorpedoes.txt)
U+FEFF
abstrato .
Poderia usar abaixo para obter UTF8 sem BOM
$MyFile | Out-File -Encoding ASCII
ASCII
não é UTF-8, mas também não é a página de código ANSI atual - você está pensando Default
; ASCII
verdadeiramente é a codificação ASCII de 7 bits, com pontos de código> = 128 sendo convertidos em ?
instâncias literais .
-Encoding ASCII
se realmente é apenas ASCII de 7 bits: 'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- o ä
foi transliterado para a ?
. Por outro lado, -Encoding Default
("ANSI") a preservaria corretamente.
Este funciona para mim (use "Padrão" em vez de "UTF8"):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
O resultado é ASCII sem BOM.
Default
codificação usará a página de código ANSI atual do sistema, que não é UTF-8, conforme necessário.