Out-File
在使用 UTF-8 时似乎会强制添加BOM:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
如何使用PowerShell以UTF-8无BOM格式编写文件?
2021年更新
自我10年前提出此问题以来,PowerShell有了一些变化。请检查下面的多个答案,它们包含很多有用的信息!
Out-File
在使用 UTF-8 时似乎会强制添加BOM:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
如何使用PowerShell以UTF-8无BOM格式编写文件?
自我10年前提出此问题以来,PowerShell有了一些变化。请检查下面的多个答案,它们包含很多有用的信息!
使用.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。 - Roman KuzminWriteAllLines
方法从 [System.Environment]::CurrentDirectory
获取当前目录。如果您在 PowerShell 中打开并更改了当前目录(使用 cd
或 Set-Location
命令),则 [System.Environment]::CurrentDirectory
不会被更改,文件将保存在错误的目录中。为了避免这种情况,您可以使用 [System.Environment]::CurrentDirectory = (Get-Location).Path
来解决。 - Shayan Toqraee[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
。 - Johny Skovdal我认为这不会是UTF,但我刚刚发现了一个似乎很简单的解决方案...
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
对我来说,这将导致生成一个没有BOM的UTF-8文件,而不管源格式是什么。
-Encoding ASCII
可以避免 BOM 问题,但显然你只能得到 _7位ASCII字符_。鉴于ASCII是UTF-8的子集,因此生成的文件在技术上也是一个有效的UTF-8文件,但输入中的所有非ASCII字符都将被转换为文字 ?
字符。 - mklement0注意:本答案适用于 Windows PowerShell;相比之下,在跨平台的 PowerShell Core 版本(v6+)中,UTF-8 无 BOM 是所有 cmdlet 的 默认编码。
换句话说:如果您使用的是 PowerShell [Core] 版本 6 或更高版本,则默认情况下会获得无 BOM 的 UTF-8 文件(您还可以使用 -Encoding utf8
/ -Encoding utf8NoBOM
显式请求,而使用 -utf8BOM
则会获得带有 BOM 的编码)。
如果您正在运行 Windows 10 或更高版本,并且您愿意在整个系统范围内切换到无 BOM 的 UTF-8 编码 - 这将产生深远的影响,但是 - 即使是 Windows PowerShell 也可以始终使用无 BOM 的 UTF-8 - 请参见 this answer。
一个简单的、(非流式) PowerShell 本地替代方案是使用 New-Item
,它 (奇怪的是) 即使在 Windows PowerShell 中默认创建没有 BOM 的 UTF-8 文件:
# 注意使用 -Raw 将文件作为整体读取。
# 与 Set-Content / Out-File 不同,不会添加任何尾随换行符。
$null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
注意: 要将任意命令的输出保存为与 Out-File
相同格式的输出,请先将其管道传输到 Out-String
; 例如:
$null = New-Item -Force Out.txt -Value (Get-ChildItem | Out-String)
为了方便起见,下面是高级的自定义函数 Out-FileUtf8NoBom
,一种基于管道的替代方案,模仿 Out-File
,这意味着:
Out-File
一样使用它。Out-File
一样发送到控制台。-UseLF
开关允许您使用 Unix 格式的 LF-only 换行符 ("`n"
),而不是通常获得的 Windows 格式的 CRLF 换行符 ("`r`n"
)。例子:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath # Add -UseLF for Unix newlines
请注意,(Get-Content $MyPath)
被括在(...)
中,这确保了整个文件在通过管道发送结果之前被打开、完全读取并关闭。这是必要的,以便能够将更新写回到相同文件(就地更新)。
然而,总的来说,这种技术不建议使用,有两个原因:(a) 整个文件必须适合内存,(b) 如果命令被中断,数据将会丢失。
关于内存使用的说明:
New-Item
替代品需要首先在内存中构建整个文件内容,这可能会在大输入集的情况下造成问题。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.
.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, 2022 Michael Klement <mklement0@gmail.com> (http://same2u.net),
released under the [MIT license](https://spdx.org/licenses/MIT#licenseText).
#>
[CmdletBinding(PositionalBinding=$false)]
param(
[Parameter(Mandatory, Position = 0)] [string] $LiteralPath,
[switch] $Append,
[switch] $NoClobber,
[AllowNull()] [int] $Width,
[switch] $UseLF,
[Parameter(ValueFromPipeline)] $InputObject
)
begin {
# 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 } }
try {
# Create the script block with the command to use in the steppable pipeline.
$scriptCmd = {
& Microsoft.PowerShell.Utility\Out-String -Stream @htOutStringArgs |
. { process { if ($UseLF) { $sw.Write(($_ + "`n")) } else { $sw.WriteLine($_) } } }
}
$steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin)
$steppablePipeline.Begin($PSCmdlet)
}
catch { throw }
}
process
{
$steppablePipeline.Process($_)
}
end {
$steppablePipeline.End()
$sw.Dispose()
}
}
$null = New-Item -Force "\$env:ProgramData\ssh\administrators_authorized_keys" -Value (Get-Content -Path "\$env:ProgramData\ssh\administrators_authorized_keys" | Out-String)
- nhooyr$null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
(速度更快,并保留现有的换行格式)- 我已更新答案。 - mklement0从版本6开始,PowerShell支持UTF8NoBOM
编码,包括set-content和out-file,甚至将其作为默认编码。
所以在上面的示例中,它应该是这样的:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
IO.FileInfo
对象(来自诸如Get-Item
的函数)和相对路径兼容。Text.UTF8Encoding
对象
- 虽然Text.UTF8Encoding
能够插入BOM,但默认情况下不会插入。GetBytes
方法将字符串转换为字节
- 确保目标字符串实际上不是一个字符串数组 - $stringVar.Count
应该等于1。Set-Content -Encoding Byte
将字节数组写入目标位置。# This is a reusable class instance object
$utf8 = New-Object Text.UTF8Encoding
$GCRaw = Get-Content -Raw -PSPath $MyPath
Set-Content -Value $utf8.GetBytes($GCRaw) -Encoding Byte -PSPath $MyPath
-Value
根据位置推断,并且通过在参数内部创建Text.UTF8Encoding
对象来进一步缩短。$GCRaw = Get-Content $MyPath -Raw
Set-Content ([Text.UTF8Encoding]::new().GetBytes($GCRaw)) -Encoding Byte -PSPath $MyPath
#NOTE#
# (New-Object Text.UTF8Encoding).GetBytes($GCRaw))
# can be used instead of
# ([Text.UTF8Encoding]::new().GetBytes($GCRaw))
# For code intended to be compact, I recommend the latter,
# not just because it's not as long, but also because its
# lack of whitespace makes it visually more distinct.
Out-File
提供的格式化功能(与 Set-Content
不同),请先将其管道传输到 Out-String
;例如,
$MyFile = Get-ChildItem | Out-String
- mklement0重要提示!:仅当文件开头的额外空格或换行符对您的使用情况没有问题时才有效,例如,如果它是SQL文件、Java文件或可读文本文件。
可以使用创建一个空(非UTF8或ASCII(UTF8兼容))文件并附加到它的组合(如果源是文件,则用 gc $src
替换 $ str ):
" " | 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
optionally: continue appending with Out-File
:
"more foo bar" | Out-File -encoding UTF8 -append $dest
此脚本将把DIRECTORY1中所有的.txt文件转换为UTF-8(无BOM),并输出到DIRECTORY2。
foreach ($i in ls -name DIRECTORY1\*.txt)
{
$file_content = Get-Content "DIRECTORY1\$i";
[System.IO.File]::WriteAllLines("DIRECTORY2\$i", $file_content);
}
旧问题,新答案:
虽然“旧”PowerShell会写入BOM,但新的跨平台变体行为不同:默认值为“无BOM”,可以通过开关进行配置:
-Encoding
指定目标文件的编码类型。 默认值为utf8NoBOM。
此参数的可接受值如下:
- ascii: 使用ASCII(7位)字符集的编码。
- bigendianunicode: 使用大端字节顺序对UTF-16格式进行编码。
- oem: 使用MS-DOS和控制台程序的默认编码。
- unicode: 使用小端字节顺序对UTF-16格式进行编码。
- utf7: 使用UTF-7格式进行编码。
- utf8: 使用UTF-8格式进行编码。
- utf8BOM: 使用带有字节顺序标记(BOM)的UTF-8格式进行编码
- utf8NoBOM: 使用不带字节顺序标记(BOM)的UTF-8格式进行编码
- utf32: 使用UTF-32格式进行编码。
对于PowerShell 5.1,启用以下设置:
控制面板,区域,管理,更改系统区域设置,使用Unicode UTF-8来支持全球语言
然后在PowerShell中输入以下内容:
$PSDefaultParameterValues['*:Encoding'] = 'Default'
另外,您可以升级到 PowerShell 6 或更高版本。
$stdcaltxt | Out-File -encoding utf8 -FilePath $stdCalFileName
和 2)Set-Content -Path $stdCalFileName -Value $stdcaltxt -Encoding utf8
两者都产生了不同的编码 UTF8-BOM 和 USC2 LE BOM,根据 Notepad++ 编码检查! - JGFMK