Out-File
UTF-8を使用するとBOMを強制するようです:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
PowerShellを使用してBOMなしでUTF-8でファイルを書き込むにはどうすればよいですか?
Out-File
UTF-8を使用するとBOMを強制するようです:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
PowerShellを使用してBOMなしでUTF-8でファイルを書き込むにはどうすればよいですか?
回答:
.NETのUTF8Encoding
クラスを使用$False
し、コンストラクターに渡すと、動作するようです。
$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)
で十分です。このWriteAllLines
オーバーロードは、BOMなしで正確にUTF8を書き込みます。
WriteAllLines
必要に思わ$MyPath
絶対的であることを。
WriteAllLines
はから現在のディレクトリを取得し[System.Environment]::CurrentDirectory
ます。PowerShellを開いて現在のディレクトリを変更すると(cd
またはを使用Set-Location
)、[System.Environment]::CurrentDirectory
変更されず、ファイルが誤ったディレクトリに置かれます。これを回避するには、を使用し[System.Environment]::CurrentDirectory = (Get-Location).Path
ます。
現在の適切な方法は、@ Roman Kuzmin が @Mへのコメントで推奨するソリューションを使用することです。ダドリーの答え:
[IO.File]::WriteAllLines($filename, $content)
(また、不要なSystem
名前空間の説明を取り除くことで少し短縮しました-デフォルトで自動的に置き換えられます。)
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
これはUTFではないだろうと思っていましたが、動作しているように見える非常に単純なソリューションを見つけました...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
私にとってこれは、ソース形式に関係なく、BOMファイルなしのutf-8になります。
-encoding utf8
要件に使用した以外は、これでうまくいきました。
-Encoding ASCII
BOMの問題は回避されますが、7ビットのASCII文字しか取得できないことは明らかです。ASCIIがUTF-8のサブセットであることを考えると、結果として得られるファイルも技術的には有効なUTF-8ファイルですが、入力内のすべての非ASCII文字はリテラル?
文字に変換されます。
-encoding utf8
しましたが、BOMでUTF-8を出力します。:(
注:この回答はWindows PowerShellに適用されます。対照的に、クロスプラットフォームPowerShellでコア版(V6 +)、UTF-8 BOMなしではある既定のエンコーディングすべてのコマンドレットを横切って、。
言い換えると、PowerShell [コア]バージョン6以降を使用している場合、デフォルトで BOMなしのUTF-8ファイルを取得します(-Encoding utf8
/ -Encoding utf8NoBOM
で明示的に要求することもできますが、 -BOMエンコーディングで取得できます-utf8BOM
)。
M.ダドリー自身のシンプルで実用的な答え(およびForNeVeRのより簡潔な再定式化)を補足するには:
便宜上、ここでは高度な機能ですOut-FileUtf8NoBom
、模倣そのパイプラインベースの代替Out-File
手段:
Out-File
パイプラインと同じように使用できます。Out-File
。例:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath
がでどのよう(Get-Content $MyPath)
に囲まれているかに注意してください(...)
。これにより、パイプラインを介して結果を送信する前に、ファイル全体が開かれ、完全に読み込まれ、閉じられます。これは、同じファイルに書き戻すことができるようにするために必要です(適切な場所に更新してください)。
ただし、一般に、この手法は2つの理由でお勧めできません。(a)ファイル全体がメモリに収まる必要がある、(b)コマンドが中断されると、データが失われる。
メモリ使用に関する注意:
Out-FileUtf8NoBom
(MITライセンスのGistとしても入手可能)のソースコード:
<#
.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()
}
}
の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()
たり類似したりすることの違いは、実際のファイルパスだけでなく、あらゆるタイプのアイテムとパスで正常に機能することです。
このスクリプトは、BOMなしのUTF-8に、DIRECTORY1内のすべての.txtファイルを変換し、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"
}
あなたが使用したい場合[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"
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
はConvert-Path $MyPath
です。末尾のCRLFを確実にしたい場合[System.IO.File]::WriteAllLines()
は、単一の入力文字列でも使用します(は必要ありませんOut-String
)。
私が利用する1つの手法は、Out-Fileコマンドレットを使用して出力をASCIIファイルにリダイレクトすることです。
たとえば、Oracleで実行する別のSQLスクリプトを作成するSQLスクリプトをよく実行します。単純なリダイレクト( ">")では、出力はSQLPlusで認識されないUTF-16になります。これを回避するには:
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
-Encoding ASCII
BOMの問題は回避されますが、7ビットのASCII文字しかサポートされません。ASCIIがUTF-8のサブセットである場合、結果として得られるファイルも技術的には有効なUTF-8ファイルですが、入力内のすべての非ASCII文字はリテラル?
文字に変換されます。
複数のファイルを拡張子でUTF-8に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)
}
何らかの理由で、WriteAllLines
呼び出しはまだBOMを生成しており、BOM UTF8Encoding
なしの引数があり、それがありません。しかし、以下は私のために働きました:
$bytes = gc -Encoding byte BOMthetorpedoes.txt
[IO.File]::WriteAllBytes("$(pwd)\BOMthetorpedoes.txt", $bytes[3..($bytes.length-1)])
それが機能するためには、ファイルパスを絶対パスにする必要がありました。それ以外の場合は、ファイルをデスクトップに書き込みました。また、BOMが3バイトであることがわかっている場合にのみ機能すると思います。エンコーディングに基づいて特定のBOM形式/長さを期待することがどの程度信頼できるかはわかりません。
また、書かれているように、これはおそらく、ファイルがPowerShell配列に収まる場合にのみ機能します。これは[int32]::MaxValue
、私のマシンよりも短い値の長さ制限があるようです。
WriteAllLines
エンコード引数がないと、BOM 自体が書き込まれることはありませんが、文字列がたまたまBOM 文字(U+FEFF
)で始まっていることが考えられます。例:$s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)
(BOMが記述され[char] 0xfeff +
ていないことを確認するには、を省略します)。
[Environment]::CurrentDirectory = $PWD.ProviderPath
か、"$(pwd)\..."
アプローチのより一般的な代替手段(より良い:"$pwd\..."
、さらに優れた:"$($pwd.ProviderPath)\..."
または(Join-Path $pwd.ProviderPath ...)
)として、(Convert-Path BOMthetorpedoes.txt)
U+FEFF
のそれぞれのエンコーディングのバイト表現です。
以下を使用してBOMなしでUTF8を取得できます
$MyFile | Out-File -Encoding ASCII
ASCII
がUTF-8ではないことは正しいですが、それは現在のANSIコードページではなく、あなたは考えていDefault
ます。ASCII
本当に7ビットASCIIエンコーディングで、コードポイント> = 128がリテラル?
インスタンスに変換されます。
-Encoding ASCII
実際には7ビットのASCIIである:'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
- ä
に音訳されています?
。対照的に、-Encoding Default
( "ANSI")はそれを正しく保持します。
これは私のために働きます(「UTF8」の代わりに「デフォルト」を使用してください):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
結果は、BOMなしのASCIIです。
Default
私は必要に応じてエンコードは、UTF-8ではありません、システムの現在のANSIコードページを使用します。