PowerShell:设置内容替换单词并编码无 BOM 的 UTF8

Sac*_*iko 3 powershell utf-8

我想将 csv 文件中的 \ 转义为 \\ 以上传到 Redshift。以下简单的 PowerShell 脚本可以按预期将 $TargetWord \ 替换为 $ReplaceWord \\ ,但使用 bom 导出 utf-8 有时会导致 Redshift 复制错误。

任何改进它的建议将不胜感激。先感谢您。

Exp_Escape.ps1

Param(
    [string]$StrExpFile,
    [string]$TargetWord,
    [string]$ReplaceWord
)

# $(Get-Content "$StrExpFile").replace($TargetWord,$ReplaceWord) | Set-Content -Encoding UTF8 "$StrExpFile"
Run Code Online (Sandbox Code Playgroud)

mkl*_*nt0 8

  • PowerShell (Core) 7+中,默认情况下您将获得无 BOM 的UTF-8 文件-Encoding utf8-Encoding utf8NoBom明确表达默认情况;-Encoding utf8BOM需要使用 BOM 。

  • 不幸的是,在Windows PowerShell中,您必须使用解决方法来获取 BOM-less UTF-8,因为仅生成带有 BOM 的-Encoding utf8UTF-8 文件(并且不支持其他相关值)。utf8

解决方法需要结合使用Out-StringNew-Item,这(奇怪的是)默认情况下会创建无 BOM 的UTF-8 文件,即使在 Windows PowerShell 中也是如此:

Param(
    [string]$StrExpFile,
    [string]$TargetWord,
    [string]$ReplaceWord
)

$null = 
  New-Item -Force $StrExpFile -Value (
    (Get-Content $StrExpFile).Replace($TargetWord, $ReplaceWord) | Out-String
  )
Run Code Online (Sandbox Code Playgroud)

笔记:

  • $null = 需要丢弃New-Item发出的输出对象(这是描述新创建的文件的文件信息对象)。

  • -Force需要这样才能用相同的名称悄悄地覆盖现有文件(默认情况下Set-Content会这样做Out-File)。

  • 参数-Value必须是要写入文件的单个(多行)字符串Out-String,这是确保的。[1]

注意事项

  • 对于非字符串输入对象,创建与默认情况下在控制台中看到的Out-String相同的丰富的显示表示形式。Out-File

  • New-Item当它将字符串写入文件时,它本身不会附加尾随换行符Out-String,但奇怪的是却这样做了;虽然这在这里很方便,但通常会出现问题,如GitHub 问题 #14444中所述。

  • 使用的替代方法是手动Out-String创建多行字符串,这有点麻烦(用于创建仅 LF 换行符,即使在 Windows 上,PowerShell 和大多数程序也乐意接受;对于平台本机换行符 (CRLF) Windows,请改用):"`n"[Environment]::NewLine

     $null = 
       New-Item -Force $StrExpFile -Value (
         ((Get-Content $StrExpFile).Replace($TargetWord, $ReplaceWord) -join "`n`") + "`n"
       )
    
    Run Code Online (Sandbox Code Playgroud)
  • 由于整个文件内容必须作为参数传递,[2]它必须作为一个整体装入内存;接下来讨论的便利函数可以避免这个问题。

有关在Windows PowerShell中使用以流方式 创建无 BOM UTF-8 文件的便捷包装函数,请参阅Out-File此答案


直接使用 .NET API 的替代方案

.NET API默认生成无 BOM 的 UTF-8 文件。
但是,由于 .NET 的工作目录通常与 PowerShell 的工作目录不同,因此必须始终使用完整文件路径,这需要更多的工作:

# In order for .NET API calls to work as expected,
# file paths must be expressed as *full, native* paths.
$OutDir = Split-Path -Parent $StrExpFile
if ($OutDir -eq '') { $OutDir = '.' }
$strExpFileFullPath = Join-Path (Convert-Path $OutDir) (Split-Path -Leaf $StrExpFile)

# Note: .NET APIs create BOM-less UTF-8 files *by default*
[IO.File]::WriteAllLines(
  $strExpFileFullPath,
  (Get-Content $StrExpFile).Replace($TargetWord, $ReplaceWord)
)
Run Code Online (Sandbox Code Playgroud)

以上就是使用的System.IO.File.WriteAllLines方法。


[1] 请注意,Out-String自动将尾随换行符附加到其输出的字符串中,这实际上是需要的(以确保文件以换行符结尾,而这New-Item本身并不能做到这一点);然而,一般来说,这种行为是有问题的,正如GitHub 问题 #14444中讨论的那样。

[2] 请注意,虽然技术上支持通过管道接收New-Item要写入文件的内容,但不幸的是,它会连续将每个内容单独写入目标文件,只有最后一个最终出现在文件中。