PowerShell - 批量更改文件编码为UTF-8

时间:2013-09-08 14:22:57

标签: powershell encoding batch-file utf-8

我正在尝试做一件简单的事情:将文件编码从任何东西更改为UTF-8而不使用BOM。我找到了几个执行此操作的脚本,唯一真正适用于我的脚本就是这一个:https://superuser.com/questions/397890/convert-text-files-recursively-to-utf-8-in-powershell#answer-397915

它按预期工作,但我需要生成的文件没有BOM。所以我尝试稍微修改脚本,添加了给出这个问题的解决方案:Using PowerShell to write a file in UTF-8 without the BOM

这是我的最终剧本:

foreach ($i in Get-ChildItem -Recurse) {
    if ($i.PSIsContainer) {
        continue
    }

    $dest = $i.Fullname.Replace($PWD, "some_folder")

    $Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)

    if (!(Test-Path $(Split-Path $dest -Parent))) {
        New-Item $(Split-Path $dest -Parent) -type Directory
    }

    get-content $i | out-file -encoding $Utf8NoBomEncoding -filepath $dest
}

问题是,对于System.Text.UTF8Encoding($False)行,powershell返回错误,抱怨参数不正确:

无法验证“编码”参数的参数。参数“System.Text.UTF8Encoding”不属于ValidateSet属性指定的“unicode,utf7,utf8,utf32,ascii”组。

我想知道我是否遗漏了一些东西,比如PowerShell版本或类似的东西。我以前从未编写过Powershell脚本,所以我完全迷失了。我需要更改这些文件编码,有数百个,我不想自己一个接一个地做。

实际上我使用的是Windows 7附带的2.0版本。

提前致谢!

编辑1

我尝试了以下代码,由@LarsTruijens和其他帖子建议:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach ($i in Get-ChildItem -Recurse) {
    if ($i.PSIsContainer) {
        continue
    }

    $dest = $i.Fullname.Replace($PWD, "some_folder")

    if (!(Test-Path $(Split-Path $dest -Parent))) {
        New-Item $(Split-Path $dest -Parent) -type Directory
    }

    $content = get-content $i
    [System.IO.File]::WriteAllLines($dest, $content, $Utf8NoBomEncoding)
}

这给了我一个异常,抱怨WriteAllLines的一个参数:"Exception on calling 'WriteAllLines' with 3 arguments. The value can't be null". Parameter name: contents。但是,该脚本会创建所有文件夹。但它们都是空的。

编辑2

关于此错误的一个有趣的事情是“content”参数不为null。如果我输出$ content变量的值(使用Write-host),则存在行。那么为什么它传递给WriteAllLines方法时变为null?

编辑3

我已经为变量添加了内容检查,因此脚本现在看起来像这样:

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach ($i in Get-ChildItem -Recurse) {
    if ($i.PSIsContainer) {
        continue
    }

    $dest = $i.Fullname.Replace($PWD, "some_folder")

    if (!(Test-Path $(Split-Path $dest -Parent))) {
        New-Item $(Split-Path $dest -Parent) -type Directory
    }

    $content = get-content $i

    if ( $content -ne $null ) {

        [System.IO.File]::WriteAllLines($dest, $content, $Utf8NoBomEncoding)
    }
    else {
        Write-Host "No content from: $i"
    }
}

现在每次迭代都会返回“No content from:$ i”消息,但该文件不为空。还有一个错误:Get-content: can't find the path 'C:\root\FILENAME.php' because it doesn't exists.似乎它试图在根目录而不是子文件夹中找到文件。它似乎能够从子文件夹中获取文件名,但尝试从root文件中读取它。

编辑4 - 最终工作版

经过一番挣扎并遵循我来到这里的建议,特别是@LarsTruijens和@AnsgarWiechers,我终于成功了。我不得不改变从$ PWD获取目录的方式,并为文件夹设置一些固定名称。在那之后,它运作得很好。

对于任何可能感兴趣的人来说,

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
$source = "path"
$destination = "some_folder"

foreach ($i in Get-ChildItem -Recurse -Force) {
    if ($i.PSIsContainer) {
        continue
    }

    $path = $i.DirectoryName -replace $source, $destination
    $name = $i.Fullname -replace $source, $destination

    if ( !(Test-Path $path) ) {
        New-Item -Path $path -ItemType directory
    }

    $content = get-content $i.Fullname

    if ( $content -ne $null ) {

        [System.IO.File]::WriteAllLines($name, $content, $Utf8NoBomEncoding)
    } else {
        Write-Host "No content from: $i"   
    }
}

7 个答案:

答案 0 :(得分:4)

你没有按照here中的完整答案。你忘记了WriteAllLines部分。

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
foreach ($i in Get-ChildItem -Recurse) {
    if ($i.PSIsContainer) {
        continue
    }

    $dest = $i.Fullname.Replace($PWD, "some_folder")

    if (!(Test-Path $(Split-Path $dest -Parent))) {
        New-Item $(Split-Path $dest -Parent) -type Directory
    }

    $content = get-content $i 
    [System.IO.File]::WriteAllLines($dest, $content, $Utf8NoBomEncoding)
}

答案 1 :(得分:2)

答案的一半在错误消息中。它告诉您Encoding参数接受的可能值,其中一个是utf8。

... out-file -encoding utf8

答案 2 :(得分:0)

  1. 转到你想要的目录cd c:\MyDirectoryWithCrazyCharacterEncodingAndUnicode
  2. 解开这个脚本吧!
  3. 复制并通过Powershell窗口中的脚本

     foreach($FileNameInUnicodeOrWhatever in get-childitem)
     {
        $FileName = $FileNameInUnicodeOrWhatever.Name
    
        $TempFile = "$($FileNameInUnicodeOrWhatever.Name).ASCII"
    
        get-content $FileNameInUnicodeOrWhatever | out-file $FileNameInUnicodeOrWhatever -Encoding ASCII 
    
        remove-item $FileNameInUnicodeOrWhatever
    
        rename-item $TempFile $FileNameInUnicodeOrWhatever
    
        write-output $FileNameInUnicodeOrWhatever "converted to ASCII ->" $TempFile
    }
    

答案 3 :(得分:0)

我已经做了一些修复

  • Get-Childitem作用于$ source
  • replace不会尝试将$ source解释为正则表达式
  • 一些解决路径
  • auto-help

并将所有内容打包到cmdlet中:

<#
    .SYNOPSIS
        Encode-Utf8

    .DESCRIPTION
        Re-Write all files in a folder in UTF-8

    .PARAMETER Source
        directory path to recursively scan for files

    .PARAMETER Destination
        directory path to write files to 
#>
[CmdletBinding(DefaultParameterSetName="Help")]
Param(
   [Parameter(Mandatory=$true, Position=0, ParameterSetName="Default")]
   [string]
   $Source,

   [Parameter(Mandatory=$true, Position=1, ParameterSetName="Default")]
   [string]
   $Destination,

  [Parameter(Mandatory=$false, Position=0, ParameterSetName="Help")]
   [switch]
   $Help   
)

if($PSCmdlet.ParameterSetName -eq 'Help'){
    Get-Help $MyInvocation.MyCommand.Definition -Detailed
    Exit
}

if($PSBoundParameters['Debug']){
    $DebugPreference = 'Continue'
}

$Source = Resolve-Path $Source

if (-not (Test-Path $Destination)) {
    New-Item -ItemType Directory -Path $Destination -Force | Out-Null
}
$Destination = Resolve-Path $Destination

$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)

foreach ($i in Get-ChildItem $Source -Recurse -Force) {
    if ($i.PSIsContainer) {
        continue
    }

    $path = $i.DirectoryName.Replace($Source, $Destination)
    $name = $i.Fullname.Replace($Source, $Destination)

    if ( !(Test-Path $path) ) {
        New-Item -Path $path -ItemType directory
    }

    $content = get-content $i.Fullname

    if ( $content -ne $null ) {
        [System.IO.File]::WriteAllLines($name, $content, $Utf8NoBomEncoding)
    } else {
        Write-Host "No content from: $i"   
    }
}

答案 4 :(得分:0)

这种方法会在将文件从当前目录复制到UTF-8之前创建整个文件夹结构。 最后,我们交换父目录名称。

$destination = "..\DestinationFolder"
Remove-item $destination -Recurse -Force
robocopy $PWD $destination /e /xf *.*

foreach($i in Get-ChildItem -Recurse) {
    if ($i.PSIsContainer) {
        continue
    }
    $originalContent = $i.Fullname
    $dest = $i.Fullname.Replace($PWD, $destination)
    if (!(Test-Path $(Split-Path $dest -Parent))) {
        New-Item $(Split-Path $dest -Parent) -type Directory
    }
    get-content $originalContent | out-file -encoding utf8 -filepath $dest
}

答案 5 :(得分:0)

当我需要UTF8编码大量的日志文件时,我改编了一些代码片段。

注意!不应与-recurse

一起使用
write-host " "
$sourcePath = (get-location).path   # Use current folder as source.
# $sourcePath = "C:\Source-files"   # Use custom folder as source.
$destinationPath = (get-location).path + '\Out'   # Use "current folder\Out" as target.
# $destinationPath = "C:\UTF8-Encoded"   # Set custom target path

$cnt = 0

write-host "UTF8 convertsation from " $sourcePath " to " $destinationPath

if (!(Test-Path $destinationPath))

{
  write-host "(Note: target folder created!) "
  new-item -type directory -path $destinationPath -Force | Out-Null
}

Get-ChildItem -Path $sourcePath -Filter *.txt | ForEach-Object {
  $content = Get-Content $_.FullName
  Set-content (Join-Path -Path $destinationPath -ChildPath $_) -Encoding UTF8 -Value $content
  $cnt++
 }
write-host " "
write-host "Totally " $cnt " files converted!"
write-host " "
pause

答案 6 :(得分:-1)

使用:

 foreach ($i in Get-ChildItem -Path $source -Recurse -Force) {

仅使用子文件夹$source中的文件。