การเปลี่ยนการเข้ารหัสเอาต์พุตเริ่มต้นของ PowerShell เป็น UTF-8


110

ตามค่าเริ่มต้นเมื่อคุณเปลี่ยนเส้นทางผลลัพธ์ของคำสั่งไปยังไฟล์หรือไพพ์ไปยังอย่างอื่นใน PowerShell การเข้ารหัสจะเป็น UTF-16 ซึ่งไม่มีประโยชน์ ฉันต้องการเปลี่ยนเป็น UTF-8

สามารถทำได้เป็นกรณี ๆ ไปโดยการแทนที่>foo.txtไวยากรณ์ด้วย| out-file foo.txt -encoding utf8แต่มันเป็นเรื่องยากที่จะต้องทำซ้ำทุกครั้ง

วิธีที่ถาวรในการตั้งสิ่งที่อยู่ใน PowerShell คือการใส่ไว้ใน\Users\me\Documents\WindowsPowerShell\profile.ps1; ฉันได้ตรวจสอบแล้วว่าไฟล์นี้ถูกเรียกใช้งานจริงเมื่อเริ่มต้น

มีการกล่าวกันว่าสามารถตั้งค่าการเข้ารหัสเอาต์พุตได้$PSDefaultParameterValues = @{'Out-File:Encoding' = 'utf8'}แต่ฉันได้ลองแล้วและไม่มีผลใด ๆ

https://blogs.msdn.microsoft.com/powershell/2006/12/11/outputencoding-to-the-rescue/ซึ่งพูดถึงการ$OutputEncodingมองแวบแรกราวกับว่ามันควรจะเกี่ยวข้อง แต่ก็พูดถึงเอาต์พุตที่เข้ารหัส ใน ASCII ซึ่งไม่ใช่สิ่งที่เกิดขึ้นจริง

คุณตั้งค่า PowerShell ให้ใช้ UTF-8 ได้อย่างไร

คำตอบ:


173

หมายเหตุ: ต่อไปนี้จะนำไปใช้กับWindows PowerShell
ดูส่วนถัดไปสำหรับข้ามแพลตฟอร์มPowerShell หลัก (v6 +)ฉบับ

  • บนPSv5.1 ขึ้นไปโดยที่>และ>>เป็นนามแฝงที่มีประสิทธิภาพOut-Fileคุณสามารถตั้งค่าการเข้ารหัสเริ่มต้นสำหรับ>/ >>/ Out-Fileผ่าน$PSDefaultParameterValuesตัวแปรการกำหนดลักษณะ :

    • $PSDefaultParameterValues['Out-File:Encoding'] = 'utf8'
  • เมื่อวันที่PSv5.0 หรือต่ำกว่าคุณไม่สามารถเปลี่ยนการเข้ารหัสสำหรับ>/>>แต่ในPSv3 หรือสูงกว่าเทคนิคดังกล่าวข้างต้นไม่Out-Fileทำงานสำหรับการโทรที่ชัดเจนในการ
    ( $PSDefaultParameterValuesตัวแปรการตั้งค่าถูกนำมาใช้ใน PSv3.0)

  • เมื่อวันที่PSv3.0 หรือสูงกว่าถ้าคุณต้องการที่จะตั้งค่าเริ่มต้นการเข้ารหัสสำหรับทุก cmdlets ที่สนับสนุนพารามิเตอร์
    -Encoding
    (ซึ่งใน PSv5.1 + รวม>และ>>) ใช้:

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

ถ้าคุณวางคำสั่งนี้ในของคุณ$PROFILE , cmdlets ดังกล่าวเป็นOut-FileและSet-Contentจะใช้เข้ารหัส UTF-8 โดยเริ่มต้น แต่ทราบว่านี้จะทำให้การตั้งค่าเซสชั่นระดับโลกที่จะส่งผลกระทบต่อคำสั่งทั้งหมด / สคริปที่ไม่ได้ระบุอย่างชัดเจนการเข้ารหัส

ในทำนองเดียวกันอย่าลืมรวมคำสั่งดังกล่าวไว้ในสคริปต์หรือโมดูลของคุณที่คุณต้องการทำงานในลักษณะเดียวกันเพื่อให้ทำงานเหมือนเดิมแม้ว่าจะเรียกใช้โดยผู้ใช้รายอื่นหรือเครื่องอื่นก็ตาม

ข้อแม้ : PowerShell ตั้งแต่ v5.1 สร้างไฟล์ UTF-8 อย่างสม่ำเสมอ _ ด้วย BOM_ (หลอก)ซึ่งเป็นเรื่องปกติในโลกของWindowsเท่านั้น- ยูทิลิตี้ที่ใช้Unixไม่รู้จัก BOM นี้ (ดูด้านล่าง) ดูโพสต์นี้สำหรับวิธีแก้ปัญหาที่สร้างไฟล์ UTF-8 ที่ไม่ใช้ BOM

สำหรับข้อมูลสรุปของพฤติกรรมการเข้ารหัสอักขระเริ่มต้นที่ไม่สอดคล้องกันอย่างสิ้นเชิงใน cmdlet มาตรฐานของ Windows PowerShell หลายรายการโปรดดูส่วนด้านล่าง


$OutputEncodingตัวแปรอัตโนมัติไม่เกี่ยวข้องกันและใช้เฉพาะกับวิธีที่ PowerShell สื่อสารกับโปรแกรมภายนอก (สิ่งที่การเข้ารหัส PowerShell ใช้เมื่อส่งสตริงไปยังพวกเขา) - ไม่มีส่วนเกี่ยวข้องกับการเข้ารหัสที่ตัวดำเนินการเปลี่ยนทิศทางเอาต์พุตและ cmdlets ของ PowerShell ใช้เพื่อบันทึกลงในไฟล์


การอ่านเพิ่มเติม: มุมมองข้ามแพลตฟอร์ม: PowerShell Core :

ขณะนี้ PowerShell เป็นแบบข้ามแพลตฟอร์มผ่านรุ่นPowerShell Coreซึ่งการเข้ารหัส - มีเหตุผล - มีค่าเริ่มต้นเป็นUTF-8 ที่ไม่ใช้ BOMซึ่งสอดคล้องกับแพลตฟอร์มที่คล้ายกับ Unix

  • ซึ่งหมายความว่าไฟล์ซอร์สโค้ดที่ไม่มี BOM จะถือว่าเป็น UTF-8 และใช้>/ Out-File/ Set-Contentดีฟอลต์เป็นUTF-8 ที่ไม่มีBOM การใช้utf8 -Encodingอาร์กิวเมนต์อย่างชัดเจนจะสร้างUTF-8 ที่ไม่ใช้ BOMด้วยเช่นกันแต่คุณสามารถเลือกที่จะสร้างไฟล์ด้วย pseudo-BOM ที่มีutf8bomค่า

  • หากคุณสร้างสคริปต์ PowerShell ด้วยตัวแก้ไขบนแพลตฟอร์มที่เหมือน Unix และในปัจจุบันแม้แต่บนWindows ที่มีโปรแกรมแก้ไขข้ามแพลตฟอร์มเช่น Visual Studio Code และ Sublime Text *.ps1ไฟล์ที่ได้มักจะไม่มี UTF-8 pseudo-BOM:

    • นี้ทำงานได้ดีบน PowerShell หลัก
    • มันอาจพังในWindows PowerShellถ้าไฟล์มีอักขระที่ไม่ใช่ ASCII ถ้าคุณไม่จำเป็นต้องใช้อักขระที่ไม่ใช่ ASCII ในสคริปต์ของคุณบันทึกเป็น UTF-8 กับ BOM
      หากไม่มี BOM Windows PowerShell (mis) จะตีความสคริปต์ของคุณว่าถูกเข้ารหัสในโค้ดหน้า "ANSI" แบบเดิม (กำหนดโดยระบบโลแคลสำหรับแอปพลิเคชันก่อน Unicode เช่น Windows-1252 ในระบบ US-English)
  • ตรงกันข้ามแฟ้มที่ทำมี UTF-8 หลอก BOM อาจเป็นปัญหาบน Unix-เช่นแพลตฟอร์มเช่นที่พวกเขาก่อให้เกิดสาธารณูปโภคยูนิกซ์เช่นcat, sedและawk- และแม้กระทั่งบางบรรณาธิการเช่นgedit- เพื่อผ่านการหลอก BOM ผ่านคือ ที่จะรักษามันเป็นข้อมูล

    • สิ่งนี้อาจไม่ใช่ปัญหาเสมอไป แต่อาจเป็นได้อย่างแน่นอนเช่นเมื่อคุณพยายามอ่านไฟล์ในสตริงbashด้วยพูดtext=$(cat file)หรือtext=$(<file)- ตัวแปรผลลัพธ์จะมี pseudo-BOM เป็น 3 ไบต์แรก

พฤติกรรมการเข้ารหัสเริ่มต้นที่ไม่สอดคล้องกันในWindows PowerShell :

น่าเสียใจที่การเข้ารหัสอักขระเริ่มต้นที่ใช้ใน Windows PowerShell ไม่สอดคล้องกันอย่างมาก PowerShell Coreรุ่นข้ามแพลตฟอร์มตามที่กล่าวไว้ในหัวข้อก่อนหน้านี้ได้ยุติสิ่งนี้อย่างน่ายกย่อง

บันทึก:

  • สิ่งต่อไปนี้ไม่ต้องการให้ครอบคลุมcmdlet มาตรฐานทั้งหมด

  • Googling ชื่อ cmdlet เพื่อค้นหาหัวข้อวิธีใช้ตอนนี้จะแสดงหัวข้อ PowerShell Coreให้คุณเป็นค่าเริ่มต้น ใช้รายการแบบหล่นลงของเวอร์ชันเหนือรายการหัวข้อทางด้านซ้ายเพื่อเปลี่ยนเป็นเวอร์ชันWindows PowerShell

  • ขณะที่เขียนนี้เอกสารไม่ถูกต้องบ่อยอ้าง ASCII ที่เข้ารหัสเริ่มต้นใน Windows PowerShell - เห็นปัญหานี้เอกสาร GitHub


Cmdlets ที่เขียน :

Out-Fileและ>/ >>สร้าง "Unicode" - UTF-16LE - ไฟล์โดยค่าเริ่มต้นซึ่งทุกอักขระช่วง ASCII (เกินไป) จะแสดงด้วย2ไบต์ซึ่งแตกต่างจากSet-Content/ Add-Content(ดูจุดถัดไป) New-ModuleManifestและExport-CliXmlยังสร้างไฟล์ UTF-16LE

Set-Content(และAdd-Contentถ้าไฟล์ยังไม่มี / ว่างเปล่า) ใช้การเข้ารหัส ANSI (การเข้ารหัสที่ระบุโดยเพจรหัสเดิม ANSI ของโลแคลระบบที่ใช้งานอยู่ซึ่ง PowerShell เรียกDefault)

Export-Csvสร้างไฟล์ ASCII ตามที่บันทึกไว้ แต่ดูหมายเหตุ-Appendด้านล่าง

Export-PSSession สร้างไฟล์ UTF-8 ด้วย BOM โดยค่าเริ่มต้น

New-Item -Type File -Value ปัจจุบันสร้าง BOM-less (!) UTF-8

Send-MailMessageหัวข้อความช่วยเหลือยังอ้างการเข้ารหัส ASCII ที่เป็นค่าเริ่มต้น - ฉันยังไม่ได้ยืนยันเองว่าการเรียกร้อง

Start-Transcript สร้างไฟล์ UTF-8 ด้วย BOM อย่างสม่ำเสมอแต่ดูหมายเหตุ-Appendด้านล่าง

คำสั่ง Re ที่ต่อท้ายไฟล์ที่มีอยู่:

>>/ Out-File -Appendทำให้ไม่มีความพยายามที่จะตรงกับการเข้ารหัสของไฟล์ที่เนื้อหาที่มีอยู่ นั่นคือพวกเขาใช้การเข้ารหัสเริ่มต้นแบบสุ่มสี่สุ่มห้าเว้นแต่จะได้รับคำแนะนำเป็นอย่างอื่นด้วย-Encodingซึ่งไม่ใช่ตัวเลือกสำหรับ>>(ยกเว้นทางอ้อมใน PSv5.1 + ผ่าน$PSDefaultParameterValuesตามที่แสดงด้านบน) กล่าวโดยย่อ: คุณต้องทราบการเข้ารหัสเนื้อหาของไฟล์ที่มีอยู่และต่อท้ายโดยใช้การเข้ารหัสเดียวกันนั้น

Add-Contentเป็นข้อยกเว้นที่น่ายกย่อง: ในกรณีที่ไม่มี-Encodingอาร์กิวเมนต์ที่ชัดเจนจะตรวจพบการเข้ารหัสที่มีอยู่และนำไปใช้กับเนื้อหาใหม่โดยอัตโนมัติ ขอบคุณ js2010 โปรดทราบว่าใน Windows PowerShell หมายความว่าเป็นการเข้ารหัส ANSI ที่ใช้หากเนื้อหาที่มีอยู่ไม่มี BOM ในขณะที่ UTF-8 ใน PowerShell Core

ความไม่ลงรอยกันระหว่างนี้Out-File -Append/ >>และAdd-Contentซึ่งยังมีผลต่อ PowerShell หลักจะมีการหารือในประเด็น GitHub นี้

Export-Csv -Append บางส่วนตรงกับการเข้ารหัสที่มีอยู่: มันต่อท้ายUTF-8 แบบสุ่มหากการเข้ารหัสของไฟล์ที่มีอยู่เป็น ASCII / UTF-8 / ANSI ใด ๆ แต่ตรงกับ UTF-16LE และ UTF-16BE อย่างถูกต้อง
หากต้องการทำให้แตกต่างกัน: ในกรณีที่ไม่มี BOM ให้Export-Csv -Appendถือว่า UTF-8 คือในขณะที่Add-Contentสมมติว่า ANSI

Start-Transcript -Append บางส่วนตรงกับการเข้ารหัสที่มีอยู่: ตรงกับการเข้ารหัสกับ BOMอย่างถูกต้องแต่ค่าเริ่มต้นคือการเข้ารหัส ASCII ที่อาจสูญเสียในกรณีที่ไม่มีการเข้ารหัส


Cmdlets ที่อ่าน (นั่นคือการเข้ารหัสที่ใช้ในกรณีที่ไม่มี BOM ):

Get-ContentและImport-PowerShellDataFileเริ่มต้นเป็น ANSI ( Default) ซึ่งสอดคล้องกับSet-Content.
ANSI ยังเป็นสิ่งที่เอ็นจิ้น PowerShell เองเริ่มต้นเมื่ออ่านซอร์สโค้ดจากไฟล์

ในทางตรงกันข้ามImport-Csv, Import-CliXmlและSelect-Stringถือว่า UTF-8 ในกรณีที่ไม่มี BOM ที่


1
มีวิธีใดในการบังคับไม่ให้นำหน้า BOM บน Win10?
mvorisek

2
ผมไม่เห็นด้วย @EliaWeiss แต่มันของ Windows PowerShell เฉพาะและในที่สุดพวกเขาไม่ได้รับมันขวาใน PowerShell หลัก
mklement0

2
@Marc: VS Code และเครื่องมือแก้ไขข้ามแพลตฟอร์มสมัยใหม่อื่น ๆ ที่น่ายกย่องโดยปริยายเป็น UTF-8 ซึ่งหมายความว่าพวกเขาจะตีความไฟล์ที่เข้ารหัส ANSI ผิด Notepad ใช้ฮิวริสติกเพื่อคาดเดาการเข้ารหัส ประเด็นก็คือมันเป็นเพียงการคาดเดาเนื่องจากไฟล์ที่เข้ารหัส UTF-8 ใด ๆ ก็เป็นไฟล์ที่เข้ารหัส ANSI ที่ถูกต้องทางเทคนิค (แต่ไม่ใช่ในทางกลับกัน) มันจะดีมากถ้าทุกอย่างบน Windows เริ่มต้นเป็น UTF-8 ในกรณีที่ไม่มี BOM เหมือนอย่างที่แพลตฟอร์ม Unix ทำ แต่นั่นไม่ใช่กรณีโดยเฉพาะอย่างยิ่งไม่ใช่ใน Windows PowerShell แม้ว่าจะโชคดีที่ตอนนี้เป็นกรณีใน PowerShell Core
mklement0

2
หากต้องการดูค่าปัจจุบันของคุณถ้ามีให้พิมพ์$PSDefaultParameterValues
Sandburg

1
@ not2qubit: chcpรายงานอะไรขึ้นอยู่กับ[Console]::InputEncoding. คุณไม่สามารถใช้chcp.comจากภายใน PowerShell ได้เนื่องจากการแคชการเข้ารหัสของ. NET แต่คุณสามารถใช้งานได้cmd.exeซึ่งจะมีผลเช่นกันหากคุณเปิด PowerShell ในภายหลังจากที่นั่น
mklement0

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.