PowerShellを使用して、BOMなしでUTF-8でファイルを書き込む


246

Out-File UTF-8を使用するとBOMを強制するようです:

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

PowerShellを使用してBOMなしでUTF-8でファイルを書き込むにはどうすればよいですか?


23
BOM =バイト順マーク。「」のように見えるファイルの先頭に配置された3つの文字(0xEF、0xBB、0xBF)
Signal15

40
これは非常にイライラします。SSH経由でファイルをアップロードしようとするなど、サードパーティのモジュールでさえ汚染されていますか?BOM!「ええ、すべてのファイルを破壊しましょう。いい考えのように思えます。」-マイクロソフト。
MichaelGG 2015

3
デフォルトのエンコーディングは、Powershellバージョン6.0 以降のUTF8NoBOMです。docs.microsoft.com/en
Paul Shiryaev

下位互換性の破壊について話す...
Dragas

回答:


220

.NETのUTF8Encodingクラスを使用$Falseし、コンストラクターに渡すと、動作するようです。

$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)

42
ああ、それが唯一の方法ではないことを願っています。
Scott Muc、

114
1行[System.IO.File]::WriteAllLines($MyPath, $MyFile)で十分です。このWriteAllLinesオーバーロードは、BOMなしで正確にUTF8を書き込みます。
ローマクズミン

6
ここでのMSDNの機能要求を作成:connect.microsoft.com/PowerShell/feedbackdetail/view/1137121/...
Groostav

3
WriteAllLines必要に思わ$MyPath絶対的であることを。
sschuberth 2017年

9
@xdhmoore WriteAllLinesはから現在のディレクトリを取得し[System.Environment]::CurrentDirectoryます。PowerShellを開いて現在のディレクトリを変更すると(cdまたはを使用Set-Location)、[System.Environment]::CurrentDirectory変更されず、ファイルが誤ったディレクトリに置かれます。これを回避するには、を使用し[System.Environment]::CurrentDirectory = (Get-Location).Pathます。
Shayan Toqraee 2017

79

現在の適切な方法は、@ Roman Kuzmin @Mへのコメントで推奨するソリューションを使用することです。ダドリーの答え

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

(また、不要なSystem名前空間の説明を取り除くことで少し短縮しました-デフォルトで自動的に置き換えられます。)


2
これは(何らかの理由で)私のBOMを削除しませんでしたが、受け入れられた回答がそうであったように
Liam

@Liam、おそらく古いバージョンのPowerShellまたは.NETですか?
ForNeVeR 2016年

1
古いバージョンの.NET WriteAllLines関数はデフォルトでBOMを書き込んだと思います。したがって、バージョンの問題である可能性があります。
Bender the Greatest

2
Powershell 3のBOMがある書き込みで確認されましたが、Powershell 4のBOMはありません。M。ダドリーの元の回答を使用する必要がありました。
chazbot7 2017年

2
したがって、デフォルトでインストールされるWindows 10で動作します。:)また、提案された改善:[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
ジョニー・スコブダル

50

これはUTFではないだろうと思っていましたが、動作しているように見える非常に単純なソリューションを見つけました...

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

私にとってこれは、ソース形式に関係なく、BOMファイルなしのutf-8になります。


8
-encoding utf8要件に使用した以外は、これでうまくいきました。
Chim Chimz 2017年

1
どうもありがとうございました。私はツールのダンプログを使用しています-ツール内にタブがありました。UTF-8が機能していませんでした。ASCIIが問題を解決しました。ありがとう。
user1529294 2017

44
はい、-Encoding ASCIIBOMの問題は回避されますが、7ビットのASCII文字しか取得できないことは明らかです。ASCIIがUTF-8のサブセットであることを考えると、結果として得られるファイルも技術的には有効なUTF-8ファイルですが、入力内のすべての非ASCII文字はリテラル?文字に変換されます
mklement0 2017

4
@ChimChimz誤ってコメントに投票-encoding utf8しましたが、BOMでUTF-8を出力します。:(
TheDudeAbides

33

注:この回答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)コマンドが中断されると、データが失われる。

メモリ使用に関する注意:

  • M.ダドリー自身の回答では、最初にファイルの内容全体をメモリに構築する必要があります。これは、大きなファイルでは問題になる可能性があります。
  • 以下の関数はこれをわずかに改善します。すべての入力オブジェクトは最初にバッファリングされますが、それらの文字列表現が生成され、出力ファイルに1つずつ書き込まれます。

Out-FileUtf8NoBomMITライセンスの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()
  }

}

16

始まって、バージョン6 PowerShellのサポートUTF8NoBOMの両方をコードセット内容アウトファイルもデフォルトエンコードとしてこれを使用しています。

上記の例では、次のようになります。

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath

@RaúlSalinas-Monteagudoどのバージョンをお使いですか?
John Bentley、

いいね。FYIでバージョンを確認$PSVersionTable.PSVersion
KCD

14

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()たり類似したりすることの違いは、実際のファイルパスだけでなく、あらゆるタイプのアイテムとパスで正常に機能することです。


5

このスクリプトは、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);
}

これは警告なしで失敗します。実行するにはどのバージョンのPowerShellを使用すればよいですか?
darksoulsong 2013

3
WriteAllLinesソリューションは、小さなファイルに最適です。ただし、より大きなファイルのソリューションが必要です。より大きなファイルでこれを使用しようとするたびに、OutOfMemoryエラーが発生します。
BermudaLamb 2015年

2
    [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" 
    }  

ソースPowerShellを使用してファイルからUTF8バイトオーダーマーク(BOM)を削除する方法


2

あなたが使用したい場合[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"

参照:ConvertTo-Csvの結果をBOMなしのUTF-8でファイルに書き込む方法


良い指針; 提案/:より簡単な代替案$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)Convert-Path $MyPathです。末尾のCRLFを確実にしたい場合[System.IO.File]::WriteAllLines()は、単一の入力文字列でも使用します(は必要ありませんOut-String)。
mklement0 2018

0

私が利用する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

4
はい、-Encoding ASCIIBOMの問題は回避されますが、7ビットのASCII文字しかサポートされません。ASCIIがUTF-8のサブセットである場合、結果として得られるファイルも技術的には有効なUTF-8ファイルですが、入力内のすべての非ASCII文字はリテラル?文字に変換されます
mklement0 2018

この回答にはさらに投票が必要です。BOMとのsqlplusの非互換性は、多くの頭痛の原因です。
アミットナイ

0

複数のファイルを拡張子で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)
}

0

何らかの理由で、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、私のマシンよりも短い値の長さ制限があるようです。


1
WriteAllLinesエンコード引数がないと、BOM 自体が書き込まれることはありませんが、文字列がたまたまBOM 文字U+FEFF)で始まっていることが考えられます。例:$s = [char] 0xfeff + 'hi'; [io.file]::WriteAllText((Convert-Path t.txt), $s)(BOMが記述され[char] 0xfeff + ていないことを確認するには、を省略します)。
mklement0 2018

1
予期せず別の場所に書き込む場合については、問題は、.NETフレームワークには通常、PowerShellとは異なる現在のディレクトリがあることです。最初にと同期する[Environment]::CurrentDirectory = $PWD.ProviderPathか、"$(pwd)\..."アプローチのより一般的な代替手段(より良い:"$pwd\..."、さらに優れた:"$($pwd.ProviderPath)\..."または(Join-Path $pwd.ProviderPath ...))として、(Convert-Path BOMthetorpedoes.txt)
mklement0

おかげで、そのような単一のBOM文字からUTF-8 BOMへの変換があることに気づきませんでした。
xdhmoore

1
すべての BOM バイトシーケンス(Unicode署名)は、実際には、抽象化された単一のUnicode文字U+FEFFのそれぞれのエンコーディングのバイト表現です。
mklement0 2018

ああ。それは物事をより簡単にするようです。
xdhmoore 2018

-2

以下を使用してBOMなしでUTF8を取得できます

$MyFile | Out-File -Encoding ASCII

4
いいえ、出力を現在のANSIコードページ(たとえば、cp1251またはcp1252)に変換します。UTF-8ではありません!
ForNeVeR 2015年

1
ロビンに感謝します。これは、BOMなしでUTF-8ファイルを書き込むために機能しなかった可能性がありますが、-Encoding ASCIIオプションはBOMを削除しました。そうすれば、gvim用のbatファイルを生成できます。.batファイルがBOMで作動していました。
Greg

3
@ForNeVeR:あなたはエンコーディングASCIIがUTF-8ではないことは正しいですが、それは現在のANSIコードページではなく、あなたは考えていDefaultます。ASCII本当に7ビットASCIIエンコーディングで、コードポイント> = 128がリテラル?インスタンスに変換されます。
mklement0

1
@ForNeVeR:あなたはおそらく「ANSI」または「拡張 ASCII」を考えています。それを検証するために、これを試してみてください-Encoding ASCII実際には7ビットのASCIIである:'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)- äに音訳されています?。対照的に、-Encoding Default( "ANSI")はそれを正しく保持します。
mklement0

3
@robこれは、UTF-8など、ASCIIとは異なるものを必要とせず、エンコーディングやUnicodeの目的を理解する必要がないすべての人にとって、完璧な答えです。すべてのASCII文字と同等のutf-8文字が同一であるため、utf-8として使用できます(ASCIIファイルをutf-8ファイルに変換すると、(BOMが取得されない場合)同一のファイルが生成されます)。テキストに非ASCII文字が含まれるすべての人にとって、この回答は誤って誤解を招くだけです。
2016

-3

これは私のために働きます(「UTF8」の代わりに「デフォルト」を使用してください):

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

結果は、BOMなしのASCIIです。


1
パーアウトファイルのドキュメントを指定するDefault私は必要に応じてエンコードは、UTF-8ではありません、システムの現在のANSIコードページを使用します。
M.ダドリー

これは、少なくともExport-CSVでは機能するようです。結果のファイルを適切なエディタで開くと、ファイルエンコーディングはBOMなしのUTF-8であり、ASCIIで期待した
とおりの

多くのエディターは、エンコードを検出できない場合、ファイルをUTF-8として開きます。
emptyother
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.