PowerShell-пакетное изменение кодировки файлов в UTF-8
Я пытаюсь сделать мертвую простую вещь: изменить кодировку файлов с чего-либо на UTF-8 без BOM. Я нашел несколько сценариев, которые делают это, и единственный, который действительно работал для меня, это: https://superuser.com/questions/397890/convert-text-files-recursively-to-utf-8-in-powershell#answer-397915.
он работал, как ожидалось, но мне нужно сгенерированные файлы без BOM. Поэтому я попытался немного изменить скрипт, добавив решение, данное этому вопросу: использование PowerShell для записи файла в UTF-8 без спецификации
это мой последний скрипт:
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)
строка, жалующаяся на неправильный параметр:
невозможно проверить аргумент в параметре' Encoding'. Аргумент " система.Текст.Utf8encoding дополнительно" не принадлежит к группе "Юникод, utf7, utf8 в, кодировках utf32, в ASCII", указанный Атрибут ValidateSet.
интересно, не пропущу ли я что-то, например, версию powershell или что-то в этом роде. Я никогда раньше не кодировал сценарий Powershell, поэтому я полностью потерян с этим. И мне нужно изменить кодировку этих файлов, их сотни, я бы не хотел делать это сам один за другим.
на самом деле я использую версию 2.0, которая поставляется с Windows 7.
спасибо заранее!
изменить 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) строки есть. Итак, почему он становится null при передаче метода WriteAllLines?
правка 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"
}
}
теперь каждая итерация возвращает сообщение" Нет содержимого из: $i", но файл не пуст. Есть еще одна ошибка: Get-content: can't find the path 'C:rootFILENAME.php' because it doesn't exists.
кажется, что он пытается найти файлы в корневом каталоге, а не в подпапки. Это, кажется, быть в состоянии чтобы получить имя файла из дочерних папок, но пытается прочитать его из root.
EDIT 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"
}
}
6 ответов
вы не следовали всему ответу в здесь. Вы забыли часть 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)
}
половина ответа находится в сообщении об ошибке. Он сообщает вам возможные значения, которые принимает параметр кодирования, одним из которых является utf8.
... out-file -encoding utf8
- Гото реж вы хотите
cd c:\MyDirectoryWithCrazyCharacterEncodingAndUnicode
- уволить этот сценарий Прочь!
скопируйте и пройдите скрипт в Windows 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
}
Я сделал некоторые исправления
- Get-Childitem действует на $source
- replace не пытается интерпретировать $source как regex
- некоторые решения-путь
- auto-help
и упаковал все в командлет:
<#
.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"
}
}
этот подход создает всю структуру папки перед копированием файлов в 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
}
С:
foreach ($i in Get-ChildItem -Path $source -Recurse -Force) {
только файлы в папке $source
будет использоваться.