313

Out-File似乎在使用 UTF-8 时强制使用 BOM:

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

如何使用 PowerShell 以 UTF-8 编写没有 BOM 的文件?

2021 年更新

自从我 10 年前写这个问题以来,PowerShell 发生了一些变化。检查下面的多个答案,他们有很多很好的信息!

4

17 回答 17

261

使用 .NET 的UTF8Encoding类并传递$False给构造函数似乎可行:

$MyRawString = Get-Content -Raw $MyPath
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding $False
[System.IO.File]::WriteAllLines($MyPath, $MyRawString, $Utf8NoBomEncoding)
于 2011-04-08T15:02:53.863 回答
97

到目前为止,正确的方法是使用@Roman Kuzmin在对@M 的评论中推荐的解决方案。达力回答

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

(我还通过去除不必要的System命名空间说明来缩短它——默认情况下它会被自动替换。)

于 2015-10-05T15:03:51.220 回答
65

我认为这不会是 UTF,但我刚刚找到了一个似乎可行的非常简单的解决方案......

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

对我来说,无论源格式如何,这都会导致没有 bom 文件的 utf-8。

于 2016-12-02T00:26:54.817 回答
49

注意:此答案适用于Windows PowerShell;相比之下,在跨平台 PowerShell Core版本 (v6+) 中,不带 BOM的 UTF-8是所有 cmdlet的默认编码。

  • 换句话说:如果您使用的是PowerShell [Core] 版本 6 或更高版本,则默认情况下会获得无 BOM 的 UTF-8 文件(您也可以使用-Encoding utf8/显式请求-Encoding utf8NoBOM,而使用-BOM 编码获得-utf8BOM)。

  • 如果您运行的是Windows 10并且您愿意在系统范围内切换到无 BOM 的 UTF-8 编码- 这可能会产生副作用 -甚至可以使Windows PowerShell始终使用无 BOM 的 UTF-8 - 请参阅此回答


为了补充M. Dudley 自己简单实用的答案(以及ForNeVeR 更简洁的重新表述):

为方便起见,这里是高级功能Out-FileUtf8NoBom一种基于管道的替代方法,它模仿Out-File,这意味着:

  • 您可以像Out-File在管道中一样使用它。
  • 不是字符串的输入对象的格式与将它们发送到控制台时的格式相同,就像使用Out-File.
  • 一个额外的-UseLF开关允许您将 Windows 样式的 CRLF 换行符转换为 Unix 样式的 LF-only 换行符。

例子:

(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath # Add -UseLF for Unix newlines

请注意如何(Get-Content $MyPath)包含在 中(...),它确保在通过管道发送结果之前打开、完整读取和关闭整个文件。为了能够写回同一个文件(就地更新它),这是必要的。
但是,通常不建议使用这种技术,原因有两个:(a) 整个文件必须适合内存;(b) 如果命令中断,数据将会丢失。

关于内存使用的说明:

  • M. Dudley 自己的回答要求首先在内存中构建整个文件内容,这对于大文件可能会产生问题。
  • 下面的函数只是稍微改进了一点:所有输入对象仍然首先被缓冲,但是它们的字符串表示然后被生成并一个一个地写入输出文件。

函数源代码Out-FileUtf8NoBom

注意:该功能也可作为 MIT 许可的 Gist 使用,并且只会在以后继续维护。

您可以使用以下命令直接安装它(虽然我个人可以向您保证这样做是安全的,但您应该始终在以这种方式直接执行之前检查脚本的内容):

# Download and define the function.
irm https://gist.github.com/mklement0/8689b9b5123a9ba11df7214f82a673be/raw/Out-FileUtf8NoBom.ps1 | iex
function Out-FileUtf8NoBom {
<#
.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-File parameters:
    * Only a literal output path is supported, and only as a parameter.
    * -Force is not supported.
    * Conversely, an extra -UseLF switch is supported for using LF-only newlines.
  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 Windows PowerShell
  lacks the ability to write UTF-8 files without a BOM: using -Encoding UTF8 
  invariably prepends a BOM.
  Copyright (c) 2017, 2020 Michael Klement <mklement0@gmail.com> (http://same2u.net), 
  released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).
#>

  [CmdletBinding()]
  param(
    [Parameter(Mandatory, Position=0)] [string] $LiteralPath,
    [switch] $Append,
    [switch] $NoClobber,
    [AllowNull()] [int] $Width,
    [switch] $UseLF,
    [Parameter(ValueFromPipeline)] $InputObject
  )

  #requires -version 3

  # Convert the input path to a full one, since .NET's working dir. usually
  # differs from PowerShell's.
  $dir = Split-Path -LiteralPath $LiteralPath
  if ($dir) { $dir = Convert-Path -ErrorAction Stop -LiteralPath $dir } else { $dir = $pwd.ProviderPath}
  $LiteralPath = [IO.Path]::Combine($dir, [IO.Path]::GetFileName($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 System.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 | % { 
      if ($UseLf) {
        $sw.Write($_ + "`n") 
      }
      else {
        $sw.WriteLine($_) 
      }
    }
  } finally {
    $sw.Dispose()
  }

}
于 2016-01-23T21:44:57.413 回答
21

版本 6开始, powershell 支持set-contentout-fileUTF8NoBOM的编码,甚至将其用作默认编码。

所以在上面的例子中,它应该是这样的:

$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
于 2019-03-09T12:59:18.457 回答
18

When using Set-Content instead of Out-File, you can specify the encoding Byte, which can be used to write a byte array to a file. This in combination with a custom UTF8 encoding which does not emit the BOM gives the desired result:

# 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

The difference to using [IO.File]::WriteAllLines() or similar is that it should work fine with any type of item and path, not only actual file paths.

于 2018-04-23T17:48:31.340 回答
5

此脚本会将 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);
}
于 2013-05-01T05:22:46.247 回答
5

老问题,新答案:

虽然“旧”powershell 编写 BOM,但与平台无关的新变体的行为确实有所不同:默认为“无 BOM”,可以通过开关进行配置:

-编码

指定目标文件的编码类型。默认值为 utf8NoBOM。

此参数的可接受值如下:

  • ascii:使用 ASCII(7 位)字符集的编码。
  • bigendianunicode:使用大端字节序以 UTF-16 格式编码。
  • oem:使用 MS-DOS 和控制台程序的默认编码。
  • unicode:使用 little-endian 字节顺序以 UTF-16 格式编码。
  • utf7:以 UTF-7 格式编码。
  • utf8:以 UTF-8 格式编码。
  • utf8BOM:使用字节顺序标记 (BOM) 以 UTF-8 格式编码
  • utf8NoBOM:以 UTF-8 格式编码,不带字节顺序标记 (BOM)
  • utf32:以 UTF-32 格式编码。

资料来源:https ://docs.microsoft.com/de-de/powershell/module/Microsoft.PowerShell.Utility/Out-File?view= powershell-7 强调我的

于 2021-03-24T13:35:15.147 回答
4

重要!:这仅适用于文件开头的额外空格或换行符对您的文件用例没有问题
(例如,如果它是 SQL 文件、Java 文件或人类可读的文本文件)

可以使用创建一个空(非 UTF8 或 ASCII(UTF8 兼容))文件并附加到它的组合(如果源是文件,则替换为)$strgc $src

" "    |  out-file  -encoding ASCII  -noNewline  $dest
$str  |  out-file  -encoding UTF8   -append     $dest

作为单线

替换$dest$str根据您的用例:

$_ofdst = $dest ; " " | out-file -encoding ASCII -noNewline $_ofdst ; $src | out-file -encoding UTF8 -append $_ofdst

作为简单的功能

function Out-File-UTF8-noBOM { param( $str, $dest )
  " "    |  out-file  -encoding ASCII  -noNewline  $dest
  $str  |  out-file  -encoding UTF8   -append     $dest
}

将它与源文件一起使用:

Out-File-UTF8-noBOM  (gc $src),  $dest

将它与字符串一起使用:

Out-File-UTF8-noBOM  $str,  $dest
  • 可选:继续附加Out-File

    "more foo bar"  |  Out-File -encoding UTF8 -append  $dest
    
于 2021-01-28T21:21:43.873 回答
2

对于 PowerShell 5.1,启用此设置:

控制面板、区域、管理、更改系统区域设置、使用 Unicode UTF-8 获得全球语言支持

然后在 PowerShell 中输入:

$PSDefaultParameterValues['*:Encoding'] = 'Default'

或者,您可以升级到 PowerShell 6 或更高版本。

https://github.com/PowerShell/PowerShell

于 2020-12-08T16:41:44.887 回答
1
    [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)

于 2017-02-08T05:47:40.030 回答
1

将多个文件通过扩展名更改为 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)
}
于 2016-10-03T13:59:08.983 回答
1

如果要使用[System.IO.File]::WriteAllLines(),则应将第二个参数强制转换为String[](如果类型$MyFileObject[]),并使用 指定绝对路径$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(),有时你应该通过管道将第二个参数| Out-String |显式地添加到每行的末尾(特别是当你使用它们时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"

请参阅:如何在没有 BOM 的情况下将 ConvertTo-Csv 的结果写入 UTF-8 文件

于 2017-05-24T13:35:09.563 回答
0

我在 PowerShell 中遇到了同样的错误,并使用了这种隔离并修复了它

$PSDefaultParameterValues['*:Encoding'] = 'utf8'
于 2022-02-02T08:27:34.270 回答
-1

我使用的一种技术是使用Out-File cmdlet将输出重定向到 ASCII 文件。

例如,我经常运行 SQL 脚本来创建另一个 SQL 脚本以在 Oracle 中执行。使用简单重定向 (">"),输出将采用 SQLPlus 无法识别的 UTF-16。要解决此问题:

sqlplus -s / as sysdba "@create_sql_script.sql" |
Out-File -FilePath new_script.sql -Encoding ASCII -Force

然后可以通过另一个 SQLPlus 会话执行生成的脚本,而无需担心 Unicode:

sqlplus / as sysdba "@new_script.sql" |
tee new_script.log

更新:正如其他人指出的那样,这将丢弃非 ASCII 字符。由于用户要求一种“强制”转换的方法,我认为他们并不关心这一点,因为他们的数据可能不包含此类数据。

如果您关心非 ASCII 字符的保存,那么这不是您的答案。

于 2016-09-22T19:36:20.903 回答
-3

可以在下面使用来获得没有 BOM 的 UTF8

$MyFile | Out-File -Encoding ASCII
于 2015-09-22T20:43:38.050 回答
-4

这个对我有用(使用“默认”而不是“UTF8”):

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

结果是没有 BOM 的 ASCII。

于 2015-05-06T12:34:44.027 回答