我有一个包含文本文件的文件夹,其中包含其他文件夹,这些文件夹也包含一些文本文件。我需要在 PowerShell 中将所有这些文件递归转换为 UTF-8 编码,并在此过程中保留文件夹结构。我试过这个:
foreach( $i in get-childitem -recurse -name ) {
get-content $i | out-file -encoding utf8 -filepath some_folder/$i
}
但它不起作用,它无法重现文件夹的层次结构。我该如何解决这个问题?
答案1
尝试一下这个。
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
}
get-content $i | out-file -encoding utf8 -filepath $dest
}
它会获取文件的完整路径,并将当前目录替换为您想要的目录。例如,您在目录C:\1\
( $PWD = C:\1\
) 中运行此命令。如果它找到文件C:\1\2\file.txt
,它会为您$dest
提供some_folder\2\file.txt
。
第一个 if 块在那里,所以您不会尝试转换目录。
如果目录不存在,则必须创建它们 - 我最初忘记了这一点。
如果您想要无 BOM 的 UTF8,请将get-content $i | out-file -encoding utf8 -filepath $dest
以下行替换为(来源):
$filecontents = Get-Content $i
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
[System.IO.File]::WriteAllLines($i, $filecontents, $Utf8NoBomEncoding)
请注意,对于较大的文件,这可能不是非常高效,因为它会先将整个文件读入内存,然后再将其再次写入。如果需要提高效率,可以逐行读取,甚至可以一次读取特定数量的字节。但是,到那时我宁愿用 C# 编写一个快速程序(因为无论如何你都会在 PS 中使用 .NET 函数)。
答案2
- 允许文件和文件夹
- 不受文件扩展名限制
- 如果目标等于路径,则覆盖原始文件
- 编码为参数
用法:&“TextEncoding.ps1”-path“c:\windows\temps\folder1”-encoding“UTF8”
这是我创建的脚本:
[CmdletBinding()]
param(
[Parameter(Mandatory=$true)]
[string]$path,
[Parameter(Mandatory=$false)]
[string]$dest = $path,
[Parameter(Mandatory=$true)]
[string]$encoding
)
function Set-Encoding(){
#ensure it is a valid path
if(-not(Test-Path -Path $path)){
throw "File or directory not found at {0}" -f $path
}
#if the path is a file, else a directory
if(Test-Path $path -PathType Leaf){
#if the provided path equals the destination
if($path -eq $dest){
#get file extension
$ext = [System.IO.Path]::GetExtension($path)
#create destination
$dest = $path.Replace([System.IO.Path]::GetFileName($path), ("temp_encoded{0}" -f $ext))
#output to file with encoding
Get-Content $path | Out-File -FilePath $dest -Encoding $encoding -Force
#copy item to original path to overwrite (note move-item loses encoding)
Copy-Item -Path $dest -Destination $path -Force -PassThru | ForEach-Object { Write-Output -inputobject ("{0} encoded {1}" -f $encoding, $_) }
#remove the extra file
Remove-Item $dest
}else{
#output to file with encoding
Get-Content $path | Out-File -FilePath $dest -Encoding $encoding -Force
}
}else{
#get all the files recursively
foreach($i in Get-ChildItem -Path $path -Recurse) {
if ($i.PSIsContainer) {
continue
}
#get file extension
$ext = [System.IO.Path]::GetExtension($i)
#create destination
$dest = "$path\temp_encoded{0}" -f $ext
#output to file with encoding
Get-Content $i.FullName | Out-File -FilePath $dest -Encoding $encoding -Force
#copy item to original path to overwrite (note move-item loses encoding)
Copy-Item -Path $dest -Destination $i.FullName -Force -PassThru | ForEach-Object { Write-Output -inputobject ("{0} encoded {1}" -f $encoding, $_) }
#remove the extra file
Remove-Item $dest
}
}
}
Set-Encoding
答案3
我必须稍微修改一下这里的脚本,才能在我的 W10/PS 5.1 上运行它
Get-ChildItem -Include *.php -Recurse | ForEach-Object {
$file = $_.FullName
$mustReWrite = $false
# Try to read as UTF-8 first and throw an exception if
# invalid-as-UTF-8 bytes are encountered.
try
{
[IO.File]::ReadAllText($file,[Text.Utf8Encoding]::new($false, $true))
}
catch [System.Text.DecoderFallbackException]
{
# Fall back to Windows-1250
$content = [IO.File]::ReadAllText($file,[Text.Encoding]::GetEncoding(1250))
$mustReWrite = $true
}
# Rewrite as UTF-8 without BOM (the .NET frameworks' default)
if ($mustReWrite)
{
Write "Converting from 1250 to UTF-8"
[IO.File]::WriteAllText($file, $content)
}
else
{
Write "Already UTF-8-encoded"
} }