12

我正在尝试做一件非常简单的事情:将文件编码从任何东西更改为没有 BOM 的 UTF-8。我发现了几个执行此操作的脚本,唯一对我有用的是这个:https ://superuser.com/questions/397890/convert-text-files-recursively-to-utf-8-in-powershell#回答 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
}

问题是powershell返回给我一个错误,关于该System.Text.UTF8Encoding($False)行,抱怨参数不正确:

无法验证“编码”参数上的参数。参数“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 变量的值(使用 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.它似乎试图在根目录而不是子文件夹中查找文件。它似乎能够从子文件夹中获取文件名,但尝试从根目录中读取它。

编辑 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"   
    }
}
4

7 回答 7

3

您没有遵循此处的全部答案。您忘记了 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)
}
于 2013-09-09T19:37:28.007 回答
2

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

... out-file -encoding utf8
于 2013-09-08T14:41:26.767 回答
1

I adapted few snipplets when I needed to UTF8 encode a massive amount of log-files.

Note! Should not be used with -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
于 2019-03-15T09:16:44.667 回答
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
}
于 2018-08-17T18:38:03.657 回答
0

我做了一些修复

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

并将所有内容打包到一个 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"   
    }
}
于 2017-12-27T14:24:07.623 回答
-1

和:

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

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

于 2016-01-28T11:43:52.623 回答
-1
  1. 转到您想要的目录cd c:\MyDirectoryWithCrazyCharacterEncodingAndUnicode
  2. 解雇这个脚本!

复制并粘贴您的 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
}
于 2017-01-27T08:02:04.140 回答