Skip to content

Start-Process stream redirection is not faithful (it eats blank lines or adds blank lines) #8702

@GeeLaw

Description

@GeeLaw

Steps to reproduce

Create a file a.cpp with the following content:

#include<cstdio>
int main()
{
    char buffer[1024];
    size_t rc;
    while ((rc = fread(buffer, 1, 1024, stdin)) != 0)
    {
        fwrite(buffer, 1, rc, stdout);
    }
    // There is a blank line on purpose!

    return 0;
}
// The file does not end with "\n" on purpose!

Then, in bash, compile it with g++ (or any C++ compiler you find on macOS) and test redirection in bash and PowerShell:

g++ a.cpp
./a.out < a.cpp > bash_redir.cpp
exec pwsh

Then in PowerShell:

Start-Process a.out -RedirectStandardInput a.cpp -RedirectStandardOutput pwsh_redir.cpp -Wait
Get-FileHash *.cpp

Expected behavior

All the three .cpp files have the same hash (are the same).

Actual behavior

The file pwsh_redir.cpp is different from the other two. The on-purpose blank line is eaten, and the on-purpose no-"\n" is broken. Specifically, pwsh_redir.cpp is the following:

#include<cstdio>
int main()
{
    char buffer[1024];
    size_t rc;
    while ((rc = fread(buffer, 1, 1024, stdin)) != 0)
    {
        fwrite(buffer, 1, rc, stdout);
    }
    // There is a blank line on purpose!
    return 0;
}
// The file does not end with "\n" on purpose!

Note that an extra "\n" has been appended at the end.

Environment data

Name                           Value
----                           -----
PSVersion                      6.1.2
PSEdition                      Core
GitCommitId                    6.1.2
OS                             Darwin 18.2.0 Darwin Kernel Version 18.2.0: Mo...
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

The full value of OS property is Darwin 18.2.0 Darwin Kernel Version 18.2.0: Mon Nov 12 20:24:46 PST 2018; root:xnu-4903.231.4~2/RELEASE_X86_64. The full table of PSCompatibleVersions is

Major  Minor  Build  Revision
-----  -----  -----  --------
1      0      -1     -1
2      0      -1     -1
3      0      -1     -1
4      0      -1     -1
5      0      -1     -1
5      1      10032  0
6      1      2      -1

Comments

The standard stream might not be textual and they are binary in many cases. E.g., the curl might write the response stream to stdout.

PowerShell for macOS does the redirection by using ForkAndExecProcess, which creates pipes connecting the parent and the child. The parent, which is PowerShell, will then copy the stream from the pipe to file in a faulty way -- it assumes textual stream (as it's using StreamReader and StreamWriter).

There are TWO faults:

  1. Assuming the streams are textual.
  2. Copying text streams with ReadLine and WriteLine carelessly, which might create extraneous or missing line breaks.

The fix is to access the underlying pipe/file stream when doing the copy, and do a binary copy (resembling the C++ example code above).

This problem does not reproduce on Windows PowerShell, because Windows PowerShell implements Start-Process stream redirection by opening the actual files, inheriting the handles and setting the correct handles in STARTUPINFO.

This problem is not related to the long-discussed broken native-piping in PowerShell.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Issue-Discussionthe issue may not have a clear classification yet. The issue may generate an RFC or may be reclassifWG-Cmdlets-Managementcmdlets in the Microsoft.PowerShell.Management module

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions