Skip to content

Resolve UTF-8, UTF-16, ASCII inconsistencies #707

Closed

Description

PS> ls > test
BASH> ls >> test
BASH> cat test # Content looks correct
PS> cat test # Content is wrong at the end

Activity

  1. added
    Usability(LEGACY) Helps filter issues that might be higher priority because they directly affect usability
    on Mar 20, 2016
  2. joeyaiello commented on Mar 24, 2016

    @joeyaiello
    Contributor

    Need to discuss this at greater length. Some thoughts:

    • There should probably be a global PS variable for setting the encoding to use
    • This global variable might be different across platforms
  3. andyleejordan commented on Apr 13, 2016

    @andyleejordan
    Member

    Joey Aiello (@joeyaiello) do we need to fix this as a bug? A mixed-encoding file is just... wrong. It's bad input. I'm not really sure what Get-ChildItem should even do about it.

  4. jpsnover commented on Apr 14, 2016

    @jpsnover
    ContributorAuthor

    CAT works so why would it be OK for Get-ChildItem to not work?
    "If you want things to work avoid the PS tools?"

    On Wed, Apr 13, 2016 at 11:06 AM, Andy Schwartzmeyer <
    [email protected]> wrote:

    Joey Aiello (@joeyaiello) https://github.com/joeyaiello do we need to fix this as a
    bug? A mixed-encoding file is just... wrong. It's bad input. I'm not really
    sure what Get-ChildItem should even do about it.

    —
    You are receiving this because you authored the thread.
    Reply to this email directly or view it on GitHub
    #707 (comment)

  5. added this to the milestone on Apr 21, 2016
  6. joeyaiello commented on May 16, 2016

    @joeyaiello
    Contributor
    • Linux uses UTF-8 without a BOM by default
    • .NET's Core implementation does not use a BOM
    • Open question: what's the default encoding on OS X?
    • PS does add a BOM to outputted files (we need to investigate exactly where)

    Linux UX sync agrees that "doing the right thing" means making sure that PowerShell's default behavior works best with the tooling and ecosystem where it exists. Therefore, we shouldn't break the existing behavior on Windows, and we should change the behavior on Linux. To "do the right thing" on Linux, we have to make sure we don't ever add a BOM on Linux.

    We have to test the non-BOM UTF-8 file generated on Linux can be read properly on Windows.

    These changes would need to be made in the following cmdlets:

    • Out-File
    • Set-Content

    We should also create an environment OR PS variable like $DefaultFileEncoding or $FileEncoding that changes the default behavior of the above cmdlets.

  7. JamesWTruher commented on May 16, 2016

    @JamesWTruher
    Collaborator

    i validated that .NET does put a BOM in the file (in a similar way that we open files), and that our cmdlets do not do anything specific to add the BOM.

    PS# $tfile = "$PWD\tfile.txt"
    PS# $utf32enc = [text.encoding]::UTF32
    PS# $fw = [io.filestream]::New($tfile,([io.filemode]::CreateNew))
    PS# $sw = [io.streamwriter]::New($fw,$utf32enc)
    PS# $sw.flush()
    PS# $sw.dispose()
    PS# $fw.dispose()
    PS# format-hex $tfile
    
               Path: F:\e\rs1d\admin\monad\src\tfile.txt
    
               00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
    00000000   FF FE 00 00 00 00 00 00 00 00 00 00 00 00 00 00  .þ..............
    
    

    It is the case that if you change to utf8 encoding the appropriate BOM is written in the file,

  8. andyleejordan commented on May 16, 2016

    @andyleejordan
    Member

    James Truher (@JamesWTruher) the results are different on Linux with .NET Core:

    Out-File

    PowerShell, on Linux, prepends a BOM when using UTF-8 encoding:

    > "hello world" | Out-File -Encoding utf8 utf8
    > file utf8
    utf8: UTF-8 Unicode (with BOM) text

    .NET Encoding

    using System;
    using System.Collections.Generic;
    using System.IO;
    using System.Text;
    
    namespace ConsoleApplication
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                String[] lines =
                    {
                        $"BufferHeight: {Console.BufferHeight}",
                        $"BufferWidth: {Console.BufferWidth}",
                        $"WindowHeight: {Console.WindowHeight}",
                        $"WindowWidth: {Console.WindowWidth}",
                        $"LargestWindowHeight: {Console.LargestWindowHeight}",
                        $"LargestWindowWidth: {Console.LargestWindowWidth}",
                        $"IsErrorRedirected: {Console.IsErrorRedirected}",
                        $"IsOutputRedirected: {Console.IsOutputRedirected}",
                        $"IsInputRedirected: {Console.IsInputRedirected}",
                        ""
                    };
    
                using (var stream = File.CreateText("Default.log"))
                {
                    foreach (var line in lines) { stream.WriteLine(line); }
                }
    
                var encodings = new Dictionary<String, Encoding>()
                    {
                        { "UTF8-Default.log", new UTF8Encoding() },
                        { "ASCII.log", new ASCIIEncoding() },
                        { "UTF8-ExplicitBOM.log", new UTF8Encoding(true) },
                        { "Unicode.log", new UnicodeEncoding() },
                    };
    
                foreach (var encoding in encodings)
                {
                    using (var file = new FileStream(encoding.Key, FileMode.Create))
                    using (var stream = new StreamWriter(file, encoding.Value))
                    {
                        foreach (var line in lines) { stream.WriteLine(line); }
                    }
                }
            }
        }
    }
    $ dotnet run
    $ for i in *.log; do file $i; done
    ASCII.log: ASCII text
    Default.log: ASCII text
    Unicode.log: Little-endian UTF-16 Unicode text
    UTF8-Default.log: ASCII text
    UTF8-ExplicitBOM.log: UTF-8 Unicode (with BOM) text
  9. andyleejordan commented on May 16, 2016

    @andyleejordan
    Member

    Obviously that text was from other experiments 😄 If you include some non-ASCII text:

                String[] lines =
                    {
                        "Normal ASCII text",
                        "你好"
                    };

    You get:

    ASCII.log: ASCII text
    Default.log: UTF-8 Unicode text
    Unicode.log: Little-endian UTF-16 Unicode text
    UTF8-Default.log: UTF-8 Unicode text
    UTF8-ExplicitBOM.log: UTF-8 Unicode (with BOM) text
    

    Which makes sense, as .NET is properly giving up on encoding non-ASCII characters for ASCII, but encodes as UTF-8 as it's supposed to:

    $ cat ASCII.log
    Normal ASCII text
    ??
    
    $ cat UTF8-Default.log
    Normal ASCII text
    你好
  10. andyleejordan commented on May 16, 2016

    @andyleejordan
    Member

    .NET Core's UTF8Encoder is specifically set to not emit a BOM by default. (Link obtained from the API browser.)

  11. lzybkr commented on May 16, 2016

    @lzybkr
    Contributor

    I think we're just using https://github.com/dotnet/coreclr/blob/master/src/mscorlib/src/System/Text/Encoding.cs/#L1542 which passes true so the behavior should be the same on Linux.

  12. andyleejordan commented on May 16, 2016

    @andyleejordan
    Member

    OMG the fact that UTF8Encoding and Encoding.UTF8 have different explicit default behaviors is absurd.

  13. andyleejordan commented on May 16, 2016

    @andyleejordan
    Member

    I just checked with Eric Erhardt (@eerhardt), and he confirmed this is the same behavior as .NET 4.6.1. The two different "default" UTF-8 constructors differ in BOM usage.

  14. 33 remaining items

  15. modified the milestone: on May 15, 2017
  16. regs01 commented on May 16, 2017

    @regs01

    My humble opinion shares some other opinions above. Default for Out-File should be UTF8 without BOM in every system.

  17. modified the milestones: , on May 22, 2017
  18. modified the milestones: , on Sep 30, 2017
  19. SteveL-MSFT commented on Sep 30, 2017

    @SteveL-MSFT
    Member

    For 6.0.0 release we'll be doing a reduced scope version of this #4878

  20. joeyaiello commented on Dec 19, 2017

    @joeyaiello
    Contributor

    James Truher (@JamesWTruher) could you clarify here what wasn't achieved by the implementation that we're shipping in 6.0?

  21. SteveL-MSFT commented on Jan 2, 2018

    @SteveL-MSFT
    Member

    The original issue from Jeffrey Snover (@jpsnover) no longer repros with RC2 with James Truher (@JamesWTruher)'s changes. I believe the change from Jim is that we default $OutputEncoding to utf-8 NoBOM instead of ASCII.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Committee-ReviewedPS-Committee has reviewed this and made a decisionIssue-Enhancementthe issue is more of a feature request than a bugResolution-FixedThe issue is fixed.WG-DevEx-Portabilityauthoring cross-platform or cross-architecture modules, cmdlets, and scriptsWG-Enginecore PowerShell engine, interpreter, and runtime

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions