提问人:sourcenouveau 提问时间:4/8/2011 最后编辑:sourcenouveau 更新时间:9/29/2023 访问量:412433
使用 PowerShell 在没有 BOM 的情况下以 UTF-8 格式写入文件
Using PowerShell to write a file in UTF-8 without the BOM
问:
Out-File
使用 UTF-8 时似乎强制执行 BOM:
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "UTF8" $MyPath
如何使用 PowerShell 以没有 BOM 的 UTF-8 格式编写文件?
2021 年更新
自从我 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
WriteAllLines
$MyPath
WriteAllLines
[System.Environment]::CurrentDirectory
cd
Set-Location
[System.Environment]::CurrentDirectory
[System.Environment]::CurrentDirectory = (Get-Location).Path
此脚本将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);
}
评论
这个对我有用(使用“默认”而不是“UTF8”):
$MyFile = Get-Content $MyPath
$MyFile | Out-File -Encoding "Default" $MyPath
结果是没有 BOM 的 ASCII。
评论
可以使用下面来获取没有 BOM 的 UTF8
$MyFile | Out-File -Encoding ASCII
评论
ASCII
Default
ASCII
?
-Encoding ASCII
'äb' | out-file ($f = [IO.Path]::GetTempFilename()) -encoding ASCII; '?b' -eq $(Get-Content $f; Remove-Item $f)
ä
?
-Encoding Default
到目前为止,正确的方法是使用 @Roman Kuzmin 在对 @M. Dudley 的评论中推荐的解决方案:
[IO.File]::WriteAllLines($filename, $content)
(我还通过剥离不必要的系统
命名空间说明来缩短它 - 默认情况下它会自动替换。
评论
[IO.File]::WriteAllLines(($filename | Resolve-Path), $content)
注意:此答案适用于 Windows PowerShell;相比之下,在跨平台 PowerShell Core 版本 (v6+) 中,不带 BOM 的 UTF-8 是所有 cmdlet 的默认编码。
换言之:如果使用的是 PowerShell [Core] 版本 6 或更高版本,则默认情况下会获得无 BOM 的 UTF-8 文件(也可以使用 / 显式请求,而使用 ) 获取 with-BOM 编码。
-Encoding utf8
-Encoding utf8NoBOM
-utf8BOM
如果您运行的是 Windows 10 或更高版本,并且您愿意在系统范围内切换到无 BOM 的 UTF-8 编码(但是,这会产生深远的影响),甚至可以使 Windows PowerShell 始终如一地使用无 BOM 的 UTF-8 - 请参阅此答案。
为了补充 M. Dudley 自己简单而务实的回答(以及 ForNeVeR 更简洁的重新表述):
一个简单的(非流式处理)PowerShell 本机替代方法是使用
New-Item
,它(奇怪地)默认创建无 BOM 的 UTF-8 文件,即使在 Windows PowerShell 中也是如此:# Note the use of -Raw to read the file as a whole. # Unlike with Set-Content / Out-File *no* trailing newline is appended. $null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
注意:要以与将相同的格式保存任意命令的输出,请先通过管道传输到
Out-String
;例如:Out-File
$null = New-Item -Force Out.txt -Value (Get-ChildItem | Out-String)
为方便起见,下面是高级自定义函数
Out-FileUtf8NoBom
,这是一种基于管道的替代方法,可模拟Out-File
,这意味着:- 您可以像在管道中一样使用它。
Out-File
- 非字符串的输入对象的格式与将它们发送到控制台时的格式相同,就像使用 一样。
Out-File
- 一个附加开关允许您使用 Unix 格式的仅 LF 换行符 () 而不是您通常获得的 Windows 格式 CRLF 换行符 ()。
-UseLF
"`n"
"`r`n"
- 您可以像在管道中一样使用它。
例:
(Get-Content $MyPath) | Out-FileUtf8NoBom $MyPath # Add -UseLF for Unix newlines
请注意 (Get-Content $MyPath
) 是如何包含在 (...)
中的,这可确保在通过管道发送结果之前打开、完整读取和关闭整个文件。这是必要的,以便能够写回同一文件(就地更新它)。
但是,通常不建议使用这种技术,原因有 2 个:(a) 整个文件必须适合内存,以及 (b) 如果命令中断,数据将丢失。
关于内存使用的注意事项:
- M. Dudley 自己的答案和上面的替代方案要求首先在内存中构建整个文件内容,这对于大型输入集来说可能会有问题。
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 <[email protected]> (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)
$null = New-Item -Force $MyPath -Value (Get-Content -Raw $MyPath)
我使用的一种技术是使用 Out-File cmdlet 将输出重定向到 ASCII 文件。
例如,我经常运行 SQL 脚本,这些脚本创建另一个要在 Oracle 中执行的 SQL 脚本。使用简单的重定向 (“>”),输出将采用 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 字符的保留,那么这不是您的答案。
评论
?
字符。-Encoding ASCII
将多个文件按扩展名更改为不带 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)
}
我以为这不会是UTF,但我只是找到了一个非常简单的解决方案,似乎有效......
Get-Content path/to/file.ext | out-file -encoding ASCII targetFile.ext
对我来说,无论源格式如何,这都会导致没有 bom 文件的 utf-8。
评论
-encoding utf8
?
字符。-Encoding ASCII
[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)
如果要使用 ,则应将第二个参数强制转换为(如果 的类型为 ),并使用 指定绝对路径,如下所示:[System.IO.File]::WriteAllLines()
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)
如果你想使用 ,有时你应该将第二个参数通过管道输送到每行的末尾,以显式地将 CRLF 添加到每行的末尾(尤其是当你将它们与 一起使用时):[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 文件
评论
$ExecutionContext.SessionState.Path.GetUnresolvedProviderPathFromPSPath($MyPath)
Convert-Path $MyPath
[System.IO.File]::WriteAllLines()
Out-String
这是公认的答案的替代方法。
此方法的优点是它与对象(来自 Get-Item
等函数)和相对路径兼容。IO.FileInfo
创建对象
Text.UTF8Encoding
- 虽然能够插入 BOM,但默认情况下不能
Text.UTF8Encoding
- 虽然能够插入 BOM,但默认情况下不能
调用对象的方法将字符串转换为字节
GetBytes
- 确保目标字符串实际上不是字符串数组 - 应等于 1
$stringVar.Count
- 确保目标字符串实际上不是字符串数组 - 应等于 1
将 the to your target with
byte array
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
从版本 6 开始,powershell 支持 set-content 和 out-file 的编码,甚至将其用作默认编码。UTF8NoBOM
所以在上面的例子中,它应该是这样的:
$MyFile | Out-File -Encoding UTF8NoBOM $MyPath
评论
$PSVersionTable.PSVersion
-Encoding UTF8NoBOM
对于 PowerShell 5.1,请启用以下设置:
控制面板、区域、管理、更改系统区域设置、使用 Unicode UTF-8 提供全球语言支持
然后在 PowerShell 中输入以下内容:
$PSDefaultParameterValues['*:Encoding'] = 'Default'
或者,可以升级到 PowerShell 6 或更高版本。
https://github.com/PowerShell/PowerShell
评论
important!:仅当开头的额外空格或换行符对于文件的用例没有问题时(例如,如果它是 SQL 文件、Java 文件或人类可读的文本文件),这才有效
可以结合创建一个空文件(非 UTF8 或 ASCII(兼容 UTF8))并附加到它(如果源文件是文件,则替换为):$str
gc $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
评论
老问题,新答案:
虽然“旧”powershell 写入 BOM,但新的与平台无关的变体的行为确实不同:默认值为“无 BOM”,可以通过交换机进行配置:
-编码
指定目标文件的编码类型。默认值为 utf8NoBOM。
此参数的可接受值如下所示:
- ascii:使用 ASCII(7 位)字符集的编码。
- bigendianunicode:使用 big-endian 字节顺序以 UTF-16 格式编码。
- oem:使用 MS-DOS 和控制台程序的默认编码。
- unicode:使用 little-endian 字节顺序以 UTF-16 格式编码。
- utf7:以 UTF-7 格式编码。
- utf8:以 UTF-8 格式编码。
- utf8BOM:以 UTF-8 格式编码,带有字节顺序标记 (BOM)
- utf8NoBOM:以 UTF-8 格式编码,不带字节顺序标记 (BOM)
- utf32:以 UTF-32 格式编码。
来源:https://learn.microsoft.com/de-de/powershell/module/Microsoft.PowerShell.Utility/Out-File?view=powershell-7 Emphasis mine
我在 PowerShell 中遇到了同样的错误,并使用了这种隔离并修复了它
$PSDefaultParameterValues['*:Encoding'] = 'utf8'
评论
$filecontent | Out-File $Filename -Encoding default
使用此方法编辑 UTF8-NoBOM 文件并生成编码正确的文件 -
$fileD = "file.xml"
(Get-Content $fileD) | ForEach-Object { $_ -replace 'replace text',"new text" } | out-file "file.xml" -encoding ASCII
起初我对这种方法持怀疑态度,但它让我感到惊讶并奏效了!
使用 powershell 版本 5.1 进行测试
我会说只使用 Set-Content 命令,不需要其他命令。
我系统中的powershell版本是:-
PS C:\Users\XXXXX> $PSVersionTable.PSVersion | fl
Major : 5
Minor : 1
Build : 19041
Revision : 1682
MajorRevision : 0
MinorRevision : 1682
PS C:\Users\XXXXX>
所以你需要类似的东西。
PS C:\Users\XXXXX> Get-Content .\Downloads\finddate.txt
Thursday, June 23, 2022 5:57:59 PM
PS C:\Users\XXXXX> Get-Content .\Downloads\finddate.txt | Set-Content .\Downloads\anotherfile.txt
PS C:\Users\XXXXX> Get-Content .\Downloads\anotherfile.txt
Thursday, June 23, 2022 5:57:59 PM
PS C:\Users\XXXXX>
现在,当我们根据屏幕截图检查文件时,它是utf8。另一个文件:.txt
PS:回答有关外来字符问题的评论查询。使用以下命令将具有外来字符的文件“testfgnchar.txt”中的内容复制到“findfnchar2.txt”。
PS C:\Users\XXXXX> Get-Content .\testfgnchar.txt | Set-Content findfnchar2.txt
PS C:\Users\XXXXX>
屏幕截图在这里。
注意:目前,存在比我在回答时使用的 PowerShell 更新的版本。
评论
set-content -encoding utf8
-encoding
echo āčķʃλшא⁴ℝ→⅛≈あ子 | set-content file.txt
set-content
如果您的第一行不包含任何不需要 UTF8 的花哨内容,则以下内容将在库存 Windows 10 Powershell 上创建一个没有 BOM 的 UTF8 文件:
$file = get-content -path "C:\temp\myfile.txt" -Encoding UTF8
# do some stuff.
$file[0] | out-file "C:\temp\mynewfile.txt" -Encoding ascii
$file | select -skip 1 | out-file "C:\temp\mynewfile.txt" -append utf8
这将使用 2 行来创建新文件。第一个使用 -encoding ascii 来强制 UTF8,但它将限制为 7 位 ascii。对于文本文件,这通常不是问题,否则您可能会选择字节编码。
第二个命令附加其余部分,但跳过第一行,因为我们已经解析了具有完全 UTF8 支持的该行。
评论