UTF-8を使用している場合、Out-File
はBOMを強制するようです。
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
PowerShellを使用してBOMなしでファイルをUTF-8で書き込む方法はありますか。
.NETの UTF8Encoding
クラスを使用して$False
をコンストラクタに渡すとうまくいくようです:
$MyFile = Get-Content $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyFile, $Utf8NoBomEncoding)
私はこれがUTFではないと考えました、しかし私はちょうどうまくいくようであるかなり簡単な解決策を見つけました...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
私にとってはこれはソースフォーマットに関係なくbomファイルなしのutf-8という結果になります。
注:この回答は、Windows PowerShell;に適用されます。対照的に、クロスプラットフォームのPowerShellCoreエディションでは、BOMなしのUTF-8はですデフォルトのエンコード。
M。ダドリー自身のシンプルで実用的な答え (および ForNeVeRのより簡潔な再定式化 )を補完するには:
便宜上、高度な関数Out-FileUtf8NoBom
、Out-File
を模倣するパイプラインベースの代替手段を次に示します。
Out-File
と同じように使用できます。Out-File
と同様に、コンソールに送信した場合と同じようにフォーマットされます。例:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
(Get-Content $MyPath)
が(...)
で囲まれていることに注意してください。これにより、ファイル全体が開かれ、完全に読み込まれ、結果がパイプラインを介して送信される前に閉じられます。これは、sameファイルに書き戻すことができるようにするために必要です(更新inplace)。
一般に、この手法は次の2つの理由からお勧めできません。(a)ファイル全体がメモリに収まる必要がある、(b)コマンドが中断された場合、データが失われる。
メモリ使用量に関するメモ:
Out-FileUtf8NoBom
のソースコード(利用可能 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()
}
}
Set-Content
の代わりにOut-File
を使用するときは、バイト配列をファイルに書き込むために使用できるエンコードByte
を指定できます。これをBOMを発行しないカスタムUTF8エンコーディングと組み合わせると、望ましい結果が得られます。
# 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
[IO.File]::WriteAllLines()
などを使うこととの違いは、実際のファイルパスだけでなく、どんな種類のアイテムやパスでもうまく動作するはずだということです。
このスクリプトは、DIRECTORY1のすべての.txtファイルをBOMなしのUTF-8に変換し、それらをDIRECTORY2に出力します。
foreach ($i in ls -name DIRECTORY1\*.txt)
{
$file_content = Get-Content "DIRECTORY1\$i";
[System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}
バージョン6以降では、powershellは set-content と out-file の両方でUTF8NoBOM
エンコーディングをサポートし、これをデフォルトのエンコーディングとしても使用します。
したがって、上記の例では、単純に次のようになります。
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
何らかの理由で、WriteAllLines
呼び出しはまだBOMなしのUTF8Encoding
引数を使用し、使用せずにBOMを作成していました。しかし、以下は私のために働いた:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
それが機能するためには、ファイルパスを絶対パスにする必要がありました。そうでなければそれは私のデスクトップにファイルを書いた。また、これはBOMが3バイトであることがわかっている場合にのみ機能すると思います。エンコーディングに基づいて特定のBOMフォーマット/長さを予測することがどれほど信頼できるかわかりません。
また、書かれているように、これはおそらくあなたのファイルがpowershell配列に収まる場合にのみうまくいくでしょう。これは私のマシンでは[int32]::MaxValue
よりも小さいいくつかの値の長さ制限があるようです。
[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"
}
私が利用しているテクニックの1つは、Out-Fileコマンドレットを使用して、出力をASCIIファイルにリダイレクトすることです。
たとえば、Oracleで実行するための別のSQLスクリプトを作成するSQLスクリプトをよく実行します。単純なリダイレクト( ">")では、出力はUTF-16になり、SQLPlusでは認識されません。これを回避するには
sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force
生成されたスクリプトは、Unicodeの心配なしに別のSQLPlusセッションを介して実行することができます。
sqlplus / as sysdba "@new_script.sql" |
tee new_script.log
BOMなしで複数のファイルを拡張子でUTF-8に変更します。
$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)
}
[System.IO.File]::WriteAllLines()
を使用したい場合は、2番目のパラメータをString[]
にキャストし($MyFile
の型がObject[]
の場合)、絶対パスを$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
で指定する必要があります。
$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)
[System.IO.File]::WriteAllText()
を使いたい場合は、2行目のパラメータを| Out-String |
にパイプしてCRLFを各行の末尾に明示的に追加する必要があります(特に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)
[Text.Encoding]::UTF8.GetBytes()
を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"