I am using PowerShell from an application written in Go, but am unable to get it to return non-ASCII characters. At first I used go-powershell, but experience the same issue: https://github.com/gorillalabs/go-powershell/issues/10 and now using a slightly more basic approach:
package main
import (
"bytes"
"fmt"
"os/exec"
)
type PowerShell struct {
powerShell string
}
func New() *PowerShell {
ps, _ := exec.LookPath("powershell.exe")
return &PowerShell{
powerShell: ps,
}
}
func (p *PowerShell) Execute(args ...string) (stdOut string, stdErr string, err error) {
args = append([]string{"-NoProfile", "-NonInteractive"}, args...)
cmd := exec.Command(p.powerShell, args...)
var stdout bytes.Buffer
var stderr bytes.Buffer
cmd.Stdout = &stdout
cmd.Stderr = &stderr
err = cmd.Run()
stdOut, stdErr = stdout.String(), stderr.String()
return
}
func main() {
posh := New()
stdout, stderr, err := posh.Execute("$OutputEncoding = [Console]::OutputEncoding; (Get-VMSwitch).Name")
fmt.Println(stdout)
fmt.Println(stderr)
if err != nil {
fmt.Println(err)
}
}
but the same happens. Instead of gettting Przełąś
, it returns Przelas
. This will result in issues when further in the code a VM is created using this Virtual Switch name. It does not get recognized and errors.
Note: $OutputEncoding = [Console]::OutputEncoding;
did not have any effect. It does get changed, but the result remains the same.
Note 2: invoking the same command directly from the command prompt does NOT have issues: powershell.exe -NoProfile -NonInteractive $OutputEncoding = [Console]::OutputEncoding; (Get-VMSwitch).Name")
or even powershell.exe -NoProfile -NonInteractive (Get-VMSwitch).Name")
. In other words, it does this only from Go when using exec.Command
.
Note 3: this is for fixing an issue with a virtual machine driver when it comes to the localized names. Yes, it could work with an GUID (.Id
) instead, but this issue persists in different parts of the system.
Man, Powershell is interesting. This was mostly the result of a bunch of trial and error.
Basically, you want to set [Console]::OutputEncoding
, not capture it.
However, to clean up after yourself, it doesn't hurt to set it back to it's original value. I haven't fully wrapped my head around it, but the change persists through multiple exec()
calls.
Here's an example:
<... everything else is as above ...>
func main() {
posh := New()
fmt.Println("With encoding change:")
stdout, stderr, err := posh.Execute(
"$test = \"Przełąś\"
" +
"$old = [Console]::OutputEncoding
" +
"[Console]::OutputEncoding = [Text.Encoding]::UTF8
" +
"[Console]::OutputEncoding
" +
"$test
" +
"[Console]::OutputEncoding = $old")
fmt.Println(stdout)
fmt.Println(stderr)
if err != nil {
fmt.Println(err)
}
fmt.Println("Without encoding change:")
stdout, stderr, err = posh.Execute(
"$test = \"Przełąś\"
" +
"[Console]::OutputEncoding
" +
"$test")
fmt.Println(stdout)
fmt.Println(stderr)
if err != nil {
fmt.Println(err)
}
}
Output:
$ ./exec-powershell.exe
With encoding change:
BodyName : utf-8
EncodingName : Unicode (UTF-8)
HeaderName : utf-8
WebName : utf-8
WindowsCodePage : 1200
IsBrowserDisplay : True
IsBrowserSave : True
IsMailNewsDisplay : True
IsMailNewsSave : True
IsSingleByte : False
EncoderFallback : System.Text.EncoderReplacementFallback
DecoderFallback : System.Text.DecoderReplacementFallback
IsReadOnly : False
CodePage : 65001
Przełąś
Without encoding change:
IsSingleByte : True
BodyName : IBM437
EncodingName : OEM United States
HeaderName : IBM437
WebName : IBM437
WindowsCodePage : 1252
IsBrowserDisplay : False
IsBrowserSave : False
IsMailNewsDisplay : False
IsMailNewsSave : False
EncoderFallback : System.Text.InternalEncoderBestFitFallback
DecoderFallback : System.Text.InternalDecoderBestFitFallback
IsReadOnly : True
CodePage : 437
Przelas