For most people, when you create a script in PowerShell you generally don't worry too much about how it is encoded. Most everything you encounter in PowerShell uses Unicode files. But when sharing files sometimes this causes problems. For example, when I post a script for download here, I need to make sure it is encoded using ASCII, otherwise formatting can get a little borked. I've also run into issues with editing the same file among different editors. Often what I need is a way to set the encoding. The manual way would be to use Notepad and select an appropriate encoding when using Save As. But that doesn't scale. What I need is a way to take a group of files and set encoding. Here's my PowerShell solution.
When I first tackled this problem I thought I was going to have to deal with arcane .NET classes. Then I remembered that Out-File has a parameter to specify encoding. All I had to do was take the content of the original file and send it to the same file with the appropriate encoding.
Technically, Out-File is creating a new file but I wanted to keep the same name. The easiest solution was to get the contents of the current file and same them to a variable. Then use that as the input for Out-File.
[cc lang="PowerShell"]
$content=Get-Content -Path $file
$filedata=Get-Item -Path $file
Write-Verbose "$(Get-Date) Saving content"
Out-File -FilePath $file -Encoding $Encoding -InputObject $content
[/cc]
One drawback, at least for me, is that results in a new file which means it has a new time stamp. Most of the time I want to leave this data alone so my Set-FileEncoding function has a parameter called -SaveTime that will take the time stamp values from the original file and copy them to the "new" file.
[cc lang="PowerShell"]
if ($SaveTime)
{
#get the "new" file
$f=Get-Item -Path $file
Write-Verbose "$(Get-Date) Retaining original time stamp values"
if ($pscmdlet.ShouldProcess("Resetting Timestamp on $file"))
{
#revert timestamp values
$f.CreationTime=$filedata.CreationTime
$f.CreationTimeUtc=$filedata.CreationTimeUtc
$f.LastAccessTime= $filedata.LastAccessTime
$f.LastAccessTimeUtc=$filedata.LastAccessTimeUtc
$f.LastWriteTime=$filedata.LastWriteTime
$f.LastWriteTimeUtc=$filedata.LastWriteTimeUtc
}
}
[/cc]
The end result is that I have a "new" copy of my file but with new encoding. Here's my function with the comment based help omitted.
[cc lang="PowerShell"]
Function Set-FileEncoding {
[cmdletBinding(SupportsShouldProcess=$True)]
ManageEngine ADManager Plus - Download Free Trial
Exclusive offer on ADManager Plus for US and UK regions. Claim now!
Param (
[Parameter(Position=0,Mandatory=$True,HelpMessage="Enter a file name and
path.",
ValueFromPipeline=$True,ValueFromPipelineByPropertyName=$True)]
[ValidateNotNullorEmpty()]
[Alias("PSPath","Name")]
[string[]]$Path,
[ValidateSet("Unicode", "UTF7", "UTF8", "UTF32",
"ASCII","BigEndianUnicode", "Default","OEM")]
[string]$Encoding="ASCII",
[switch]$SaveTime,
[switch]$Passthru
)
Begin
{
Write-Verbose "$(Get-Date) Starting $($myinvocation.command)"
}
Process
{
foreach ($file in $path)
{
Write-Verbose "$(Get-Date) Setting encoding on $File to $Encoding"
Write-Verbose "$(Get-Date) Getting content"
$content=Get-Content -Path $file
$filedata=Get-Item -Path $file
Write-Verbose "$(Get-Date) Saving content"
Out-File -FilePath $file -Encoding $Encoding -InputObject $content
if ($SaveTime)
{
#get the "new" file
$f=Get-Item -Path $file
Write-Verbose "$(Get-Date) Retaining original time stamp values"
if ($pscmdlet.ShouldProcess("Resetting Timestamp on $file"))
{
#revert timestamp values
$f.CreationTime=$filedata.CreationTime
$f.CreationTimeUtc=$filedata.CreationTimeUtc
$f.LastAccessTime= $filedata.LastAccessTime
$f.LastAccessTimeUtc=$filedata.LastAccessTimeUtc
$f.LastWriteTime=$filedata.LastWriteTime
$f.LastWriteTimeUtc=$filedata.LastWriteTimeUtc
}
}
if ($PassThru)
{
Get-Item -Path $file
}
} #foreach
} #process
End
{
Write-Verbose "$(Get-Date) Ending $($myinvocation.command)"
}
} #end function
[/cc]
The default encoding is set to ASCII but you can specify any encoding supported by Out-File. The function does not write anything to the pipeline. But you can use -Passthru and then the function will write the file object to the pipeline. Here are some examples:
[cc lang="PowerShell"]
PS C:\> Set-FileEncoding c:\files\data.txt -encoding Unicode
PS C:\> dir c:\scripts\*.ps1 | set-fileencoding -savetime
[/cc]
I hope it goes with out saying but don't try to use this with anything other than text files. Otherwise you will end up with corrupt files. If you can open the file and read it in Notepad then it should be fine.
Download Set-FileEncoding.ps1.
Good post on fixing encoding issues. One of the main culprits for creating non-ASCII file is Powershell ISE which defaults to Unicode Big Endian. Oisin has a nice post on how to fix the default http://www.nivot.org/2010/05/21/PowerShellISEHackingChangeDefaultSaveEncodingToASCII.aspx
My approach to the problem of exisitng files is to only update files that are not ASCII encoded. In that effort wrote a function to determine the encoding of a file:
http://poshcode.org/2075
Using the funciton I’ll update encoding as follows:
Get-ChildItem *.ps1 | select FullName, @{n=’Encoding’;e={Get-FileEncoding $_.FullName}} | where {$_.Encoding -ne ‘ASCII’} | foreach {(get-content $_.FullName) | set-content $_.FullName -Encoding ASCII}
I was thinking I needed a corresponding Get-FileEncoding function but you already have one. Great.
Hi, I’m actually trying to comment about your https://jdhitsolutions.com/blog/2010/07/wbadmin-demo/ posting, but I don’t see how to leave a comment there (I feel like an idiot…)
Anyway, thanks for this, I’m starting to use it on my servers here. I did notice that the “set backupshare” line seemed to need two slashes at the beginning of the UNC. I noticed that when I was editing the batch file and wondered, but it wouldn’t create the folder until I added a second slash.
I was expecting an easy WBADMIN command option to perform a full backup. I thought -vssFull was going to do it, but then it still wanted drive letters on the command line. I just listed my three drives, but I’m thinking it probably isn’t doing a full backup like I’m used to with ntbackup. (missing systemstate?)
I haven’t been very regular about doing manual backups since I upgraded, so this should be a big help. I kept trying to use Windows Backup to schedule a daily backup to a network drive, start ranting at Microsoft for leaving that out, and then just run a manual backup.
thanks again,
-John
I would also recommend trying all of this on a test server and doing a test restore so you can see exactly what is getting backed up.