Take the 2-minute tour ×
Super User is a question and answer site for computer enthusiasts and power users. It's 100% free, no registration required.

I have a folder with text files which includes other folders in it, and these also contain some text files. I need to recursively convert all these files to UTF-8 encoding in PowerShell and preserve the folder structure during this process. I have tried this:

foreach( $i in get-childitem -recurse -name ) {
    get-content $i | out-file -encoding utf8 -filepath some_folder/$i
}

But it doesn't work, it can't reproduce the hierarchy of folders. How do I cope with this problem?

share|improve this question
1  
Which version of PowerShell is this? In the one that came with (my copy of) Win7 (v2?), there's Get-ChildItem but not Get-Children... –  Bob Mar 7 '12 at 13:12
    
It was a misspelling, I've corrected it. –  Roman Mar 7 '12 at 13:35

2 Answers 2

up vote 4 down vote accepted

Try this one.

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
}

It grabs the full path of the file and replaces the current directory with the one you want. For example, you run this command in the directory C:\1\ ($PWD = C:\1\). If it finds the file C:\1\2\file.txt, it'll give you a $dest of some_folder\2\file.txt.

The first if block is there so you don't try to convert a directory.

The directories have to be created if they don't already exist - I originally forgot that.


If you want UTF8 without BOM, replace the get-content $i | out-file -encoding utf8 -filepath $dest line with the following (source):

$filecontents = Get-Content $i
$Utf8NoBomEncoding = New-Object System.Text.UTF8Encoding($False)
[System.IO.File]::WriteAllLines($i, $filecontents, $Utf8NoBomEncoding)

Note that this may not be very performant for larger files, since it reads entire files into memory before writing them again. If efficiency is needed, it is possible to read line by line, or even a specific number of bytes at a time. However, I'd rather just write a quick program in C# by that point (since you'd be using .NET fuctions in PS anyway).

share|improve this answer
    
It does not work for me. It says there is an error in line 6 ($dest etc...). It tries to call a method with a NULL argument and its wrong. –  Roman Mar 7 '12 at 13:58
    
I haven't used PowerShell enough, apparently.. the quotes were not necessary. And the directories must be created if they don't already exist. I've edited it to fix that, and actually tested it this time (more than just echoing the paths). Not sure how you got that error, though, what did you replace some_folder with? –  Bob Mar 7 '12 at 14:30
    
@Roman, You need to define $PWD, else $PWD is NULL. If your files are stored in "X:\txt_Files" and you want to store the converted files in "X:\UTF_Files", then add: $PWD = "X:\txt_Files" and replace "some_folder" with "X:\UTF_Files" –  Martin Jul 11 '13 at 17:12
    
@Martin $PWD is supposed to automatically refer to the current working directory. "some_folder" does have to be replaced with the appropriate folder. –  Bob Jul 20 '13 at 4:49
    
@Bob It worked as expected, except the fact it throws some errors about some files that don't exists, because it tries to find the file on a invalid location. What if I want to convert files to UTF-8 without BOM? What do I have to add to this script? –  darksoulsong Sep 8 '13 at 13:26
  • Allows for files and folders
  • File extension agnostic
  • Overwrites original file if destination equals the path
  • Encoding as a parameter

Usage: &"TextEncoding.ps1" -path "c:\windows\temps\folder1" -encoding "UTF8"

Here is the script I created:

[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
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.