Zmiana domyślnego kodowania danych wyjściowych programu PowerShell na UTF-8

110

Domyślnie, gdy przekierowujesz dane wyjściowe polecenia do pliku lub potokujesz je do czegoś innego w PowerShell, kodowanie to UTF-16, co nie jest przydatne. Chcę zmienić to na UTF-8.

Można to zrobić indywidualnie dla każdego przypadku, zastępując >foo.txtskładnię przez, | out-file foo.txt -encoding utf8ale powtarzanie tego za każdym razem jest niewygodne.

Trwałym sposobem ustawiania rzeczy w PowerShell jest umieszczanie ich \Users\me\Documents\WindowsPowerShell\profile.ps1; Sprawdziłem, że ten plik jest rzeczywiście wykonywany podczas uruchamiania.

Mówiono, że kodowanie wyjściowe można ustawić za pomocą, $PSDefaultParameterValues = @{'Out-File:Encoding' = 'utf8'}ale próbowałem tego i nie przyniosło to żadnego efektu.

https://blogs.msdn.microsoft.com/powershell/2006/12/11/outputencoding-to-the-rescue/, który $OutputEncodingna pierwszy rzut oka mówi o tym, że na pierwszy rzut oka powinien być odpowiedni, ale potem mówi o kodowaniu wyjścia w ASCII, co tak naprawdę się nie dzieje.

Jak ustawić program PowerShell do korzystania z UTF-8?

rwallace
źródło

Odpowiedzi:

173

Uwaga: Poniższe informacje dotyczą programu Windows PowerShell .
Zobacz następną sekcję dotyczącą wieloplatformowej wersji programu PowerShell Core (v6 +) .

  • Na PSv5.1 lub nowszym , gdzie >i >>są efektywnymi aliasami Out-File, możesz ustawić domyślne kodowanie dla >/ >>/ Out-Filepoprzez $PSDefaultParameterValueszmienną preferencji :

    • $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
  • Na PSv5.0 lub poniżej , to nie można zmienić kodowanie >/>> , ale na PSv3 lub wyższy , powyższa technika czyni pracę dla jawnych wywołańOut-File .
    ( $PSDefaultParameterValuesZmienna preferencji została wprowadzona w PSv3.0).

  • Na PSv3.0 lub wyższy , jeśli chcesz ustawić domyślne kodowanie dla wszystkich apletów poleceń, które obsługują
    to -Encodingparametr
    (co obejmuje w PSv5.1 + >i >>), przeznaczenie:

    • $PSDefaultParameterValues['*:Encoding'] = 'utf8'

Jeśli umieścisz to polecenie w swoich poleceniach$PROFILE cmdlet, takich jak Out-FileiSet-Content będą domyślnie używać kodowania UTF-8, ale pamiętaj, że powoduje to ustawienie globalne sesji, które będzie miało wpływ na wszystkie polecenia / skrypty, które nie określają jawnie kodowania.

Podobnie, pamiętaj, aby w swoich skryptach lub modułach uwzględnić takie polecenia, które chcesz, aby zachowywały się w ten sam sposób , aby rzeczywiście zachowywały się tak samo, nawet jeśli są uruchamiane przez innego użytkownika lub inną maszynę.

Uwaga : PowerShell, począwszy od wersji 5.1, niezmiennie tworzy pliki UTF-8 _ z (pseudo) BOM_ , co jest zwyczajowe tylko w świecie Windows - narzędzia oparte na Uniksie nie rozpoznają tego BOM (patrz poniżej); zobacz ten post, aby poznać obejścia, które powodują tworzenie plików UTF-8 bez BOM.

Dla podsumowania szalenie niespójne domyślnego kodowania znaków zachowanie całej wiele standardowych poleceń cmdlet Windows PowerShell , zobacz dolną część.


$OutputEncodingZmienna automatyczna jest niepowiązana i dotyczy tylko sposobu, w jaki PowerShell komunikuje się z programami zewnętrznymi (jakiego kodowania używa PowerShell podczas wysyłania do nich ciągów) - nie ma nic wspólnego z kodowaniem używanym przez operatory przekierowania danych wyjściowych i polecenia cmdlet programu PowerShell do zapisywania w plikach.


Czytanie opcjonalne: perspektywa międzyplatformowa: PowerShell Core :

PowerShell jest teraz wieloplatformowy , za pośrednictwem wersji PowerShell Core , której kodowanie - rozsądnie - jest domyślnie ustawione na UTF-8 bez BOM , zgodnie z platformami typu Unix.

  • Oznacza to, że pliki kodu źródłowego bez BOM przyjmowane są za UTF-8, a przy użyciu >/ Out-File/ Set-Contentdomyślnie BOM mniej UTF-8; jawne użycie utf8 -Encodingargumentu również tworzy kod UTF-8 bez BOM , ale możesz zdecydować się na tworzenie plików z pseudo-BOM z utf8bomwartością.

  • Jeśli tworzysz skrypty PowerShell za pomocą edytora na platformie uniksopodobnej, a obecnie nawet w systemie Windows z edytorami wieloplatformowymi, takimi jak Visual Studio Code i Sublime Text, wynikowy *.ps1plik zazwyczaj nie będzie miał pseudo-BOM UTF-8:

    • Działa to dobrze w programie PowerShell Core .
    • Może się zepsuć w programie Windows PowerShell , jeśli plik zawiera znaki spoza zestawu ASCII; jeśli musisz używać znaków spoza zestawu ASCII w swoich skryptach, zapisz je jako UTF-8 z BOM .
      Bez BOM program Windows PowerShell (mis) interpretuje skrypt jako zakodowany na starszej stronie kodowej „ANSI” (określonej przez ustawienia regionalne systemu dla aplikacji poprzedzających Unicode, np. Windows-1252 w systemach amerykańsko-angielskich).
  • Odwrotnie, pliki, które zrobienia mieć UTF-8 pseudo-BOM może być problematyczne na uniksowych platformach, ponieważ przyczyna narzędzi uniksowych takich jak cat, sedi awk- a nawet niektórzy redaktorzy takie jak gedit- aby zdać pseudo-LM dzięki , czyli traktować to jako dane .

    • Nie zawsze może to stanowić problem, ale na pewno może tak być, na przykład przy próbie wczytania pliku do łańcucha bashz, powiedzmy, text=$(cat file)lub text=$(<file)- zmienna wynikowa będzie zawierała pseudo-BOM jako pierwsze 3 bajty.

Niespójne domyślne zachowanie kodowania w programie Windows PowerShell :

Niestety, domyślne kodowanie znaków używane w programie Windows PowerShell jest bardzo niespójne; wieloplatformowa edycja PowerShell Core , jak omówiono w poprzedniej sekcji, chwalebnie położyła to i zakończyła.

Uwaga:

  • Poniższe aspiracje nie obejmują wszystkich standardowych poleceń cmdlet.

  • Wyszukiwanie w Google nazw poleceń cmdlet w celu znalezienia ich tematów pomocy teraz pokazuje domyślnie wersję PowerShell Core tematów; użyj listy rozwijanej wersji nad listą tematów po lewej stronie, aby przełączyć się na wersję programu Windows PowerShell .

  • W chwili pisania tego tekstu dokumentacja często błędnie twierdzi, że ASCII jest domyślnym kodowaniem w programie Windows PowerShell - zobacz ten problem z dokumentacją GitHub .


Polecenia cmdlet, które piszą :

Out-Filei >/ >>utwórz „Unicode” - UTF-16LE - pliki domyślnie - w których każdy znak z zakresu ASCII (także) jest reprezentowany przez 2 bajty - co znacznie różni się od Set-Content/ Add-Content(patrz następny punkt); New-ModuleManifesta Export-CliXmltakże tworzyć pliki UTF-16LE.

Set-Content(a Add-Contentjeśli plik jeszcze nie istnieje / jest pusty) używa kodowania ANSI (kodowanie określone przez starszą stronę kodową ANSI aktywnego ustawienia regionalnego, którą wywołuje program PowerShell Default).

Export-Csvrzeczywiście tworzy pliki ASCII, zgodnie z dokumentacją, ale zobacz uwagi -Appendponiżej.

Export-PSSession domyślnie tworzy pliki UTF-8 z BOM.

New-Item -Type File -Value obecnie tworzy bez BOM (!) UTF-8.

W Send-MailMessagetemacie pomocy podano również, że kodowanie ASCII jest domyślne - osobiście nie zweryfikowałem tego twierdzenia.

Start-Transcript niezmiennie tworzy pliki UTF-8 z BOM, ale zobacz uwagi -Appendponiżej.

Ponownie polecenia dołączane do istniejącego pliku:

>>/ Out-File -AppendZrobić żadnej próby pasujące kodowanie pliku w istniejącej zawartości . Oznacza to, że ślepo stosują swoje domyślne kodowanie, chyba że poinstruowano inaczej -Encoding, co nie jest opcją z >>( z wyjątkiem pośrednio w PSv5.1 +, przez $PSDefaultParameterValues, jak pokazano powyżej). W skrócie: musisz znać kodowanie zawartości istniejącego pliku i dołączyć przy użyciu tego samego kodowania.

Add-Contentjest chwalebnym wyjątkiem: w przypadku braku wyraźnego -Encodingargumentu wykrywa istniejące kodowanie i automatycznie stosuje je do nowej treści. Dzięki, js2010 . Należy zauważyć, że w programie Windows PowerShell oznacza to, że jest to kodowanie ANSI, które jest stosowane, jeśli istniejąca zawartość nie ma BOM, podczas gdy w programie PowerShell Core jest to UTF-8.

Ta niespójność między Out-File -Append/ >>i Add-Content, która ma również wpływ na program PowerShell Core , została omówiona w tym problemie w usłudze GitHub .

Export-Csv -Append częściowo pasuje do istniejącego kodowania: ślepo dołącza UTF-8, jeśli kodowanie istniejącego pliku jest dowolne z ASCII / UTF-8 / ANSI, ale poprawnie pasuje do UTF-16LE i UTF-16BE.
Ujmując to inaczej: w przypadku braku BOM Export-Csv -Appendzakłada , że UTF-8 jest, podczas gdy Add-Contentzakłada ANSI.

Start-Transcript -Append częściowo pasuje do istniejącego kodowania: prawidłowo dopasowuje kodowanie z BOM , ale domyślnie stosuje potencjalnie stratne kodowanie ASCII w przypadku jego braku.


Polecenia cmdlet, które odczytują (to znaczy kodowanie używane w przypadku braku BOM ):

Get-Contenti Import-PowerShellDataFiledomyślnie ANSI ( Default), co jest zgodne z Set-Content.
ANSI jest również tym, co sam aparat PowerShell domyślnie przyjmuje, gdy odczytuje kod źródłowy z plików.

Natomiast Import-Csv, Import-CliXmli Select-Stringzakładamy, UTF-8 w przypadku braku BOM.

mklement0
źródło
1
Czy jest jakiś sposób, aby wymusić niepodawanie BOM na początku Win10?
mvorisek
2
Nie zgadzam się, @EliaWeiss, ale jest to w szczególności Windows PowerShell i ostatecznie udało im się to dobrze w PowerShell Core .
mklement0
2
@Marc: VS Code i inne nowoczesne edytory wieloplatformowe chwalą się domyślnie na UTF-8, co jednak oznacza, że ​​będą błędnie interpretować pliki zakodowane w ANSI. Notatnik używa heurystyki do odgadywania kodowania. Chodzi o to, że to tylko przypuszczenie , ponieważ każdy plik zakodowany w UTF-8 jest również technicznie poprawnym plikiem zakodowanym w ANSI (ale nie odwrotnie). Byłoby wspaniale, gdyby wszystko w systemie Windows domyślnie ustawiało się na UTF-8 przy braku BOM, tak jak robią to platformy typu Unix, ale tak nie jest, zwłaszcza nie w Windows PowerShell, chociaż na szczęście tak jest teraz w PowerShell Core.
mklement0
2
Aby zobaczyć swoją aktualną wartość, jeśli jakieś, po prostu wpisz$PSDefaultParameterValues
Sandburg
1
@ not2qubit: Od których chcpraportów zależy wyłącznie [Console]::InputEncoding. Nie możesz używać chcp.comz wnętrza PowerShell, ze względu na buforowanie kodowania .NET, ale możesz go używać cmd.exe, gdzie jest również skuteczny, jeśli później uruchomisz PowerShell z tego miejsca.
mklement0
2

Krótko mówiąc, użyj:

write-output "your text" | out-file -append -encoding utf8 "filename"
pbies
źródło