Getting an extra "$ " before my program output on Unix [#IP1]

I’m trying to get output from, say, g++.exe which is in my PATH. It works fine in Windows (minus the fact that it inserts two extra newlines for no apparent reason), but when the Unix tester runs it, it gets this extra $ before the output. What is wrong?

Output when g++.exe is run successfully (Windows):

$ g++ -Wall “C:\Users\xxx\Desktop\test.cpp” -o “C:\Users\xxx\Desktop\program.exe”


$

Output when g++.exe is run unsuccessfully (Windows):

$ g++
g++.exe: fatal error: no input files
compilation terminated.


$

Output from the Unix test machine:

remote: [your-program] $ custom_exe_2682 Maria Alice
remote: [your-program] $ Program was passed 3 args (including program name).
remote: [tester::#IP1] ^ Line does not match expected value.
remote: [tester::#IP1] Expected: “Program was passed 3 args (including program name).”
remote: [tester::#IP1] Received: “$ Program was passed 3 args (including program name).”
remote: [your-program] Arg #0 (program name): /tmp/bee/custom_exe_2682
remote: [your-program] Arg #1: Maria
remote: [your-program] Arg #2: Alice
remote: [your-program] Program Signature: 5853545686
remote: [your-program]
remote: [your-program]
remote: [tester::#IP1] Assertion failed.
remote: [tester::#IP1] Test failed

Code:

if (!receivingProgramOutput) { Console.Write("$ "); }

//...

if (result == Builtins.Result.INVALID_METHOD_NAME)
{
    string filePath = u.FindExecutableFromPath(command);

    if (filePath != null)
    {
        Process p = new Process
        {
            StartInfo = new ProcessStartInfo
            {
                FileName = filePath,
                Arguments = args != null ? String.Join(" ", args) : "",
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                UseShellExecute = false,
                CreateNoWindow = true
            }
        };

        receivingProgramOutput = true;
        p.OutputDataReceived += (s, e) => { Console.WriteLine(e.Data); };
        p.ErrorDataReceived += (s, e) => { Console.Error.WriteLine(e.Data); };
        p.Start();
        p.BeginOutputReadLine();
        p.BeginErrorReadLine();
        p.WaitForExit(5000);
        receivingProgramOutput = false;

    } else
    {
        Console.WriteLine(command + commandNotFoundError);
    }   
} else if (result == Builtins.Result.INVALID_PARAM_LENGTH)
{
    Console.WriteLine(b.GetBuiltinHelp(command));
}

Not that familiar with C# process facilities, but looks like you are taking the redirecting output and using Console.WriteLine to output it which would output an extra newline per “chunk”. Chunk could even be line-buffered versus otherwise-buffered depending various circumstances. Okay, OutputDataReceived by docs only get lines, but presumably that line already includes newline terminator, so using the WriteLine version of writing still makes an extra.

If I don’t do .WriteLine(), the output becomes combined all onto one line, like this:

java [options] [args…] (to execute a class) or java [options] -jar [args…] (to execute a jar file) or java [options] -m [/] [args…] java [options] --module [/] [args…] (to execute the main class in a module) or java [options] [args] (to execute a single source-file program) Arguments following the main class, source file, -jar , -m or --module / are passed as the arguments to main class. where options include: -cp <class search path of directories and zip/jar files> -classpath <class search path of directories and zip/jar files> --class-path <class search path of directories and zip/jar files> A ; separated list of directories, JAR archives, and ZIP archives to search for class files. -p --module-path … A ; separated list of elements, each element is a file path to a module or a directory containing modules. Each module is either a modular JAR or an exploded-module directory. --upgrade-module-path … A ; separated list of elements, each element is a file path to a module or a directory containing modules to replace upgradeable modules in the runtime image. Each module is either a modular JAR or an exploded-module directory. --add-modules [,…] root modules to resolve in addition to the initial module. can also be ALL-DEFAULT, ALL-SYSTEM, ALL-MODULE-PATH. --enable-native-access [,…] modules that are permitted to perform restricted native operations. can also be ALL-UNNAMED. --list-modules list observable modules and exit -d --describe-module describe a module and exit --dry-run create VM and load main class but do not execute main method. The --dry-run option may be useful for validating the command-line options such as the module system configuration. --validate-modules validate all modules and exit The --validate-modules option may be useful for finding conflicts and other errors with modules on the module path. -D= set a system property -verbose:[class|module|gc|jni] enable verbose output for the given subsystem -version print product version to the error stream and exit --version print product version to the output stream and exit -showversion print product version to the error stream and continue --show-version print product version to the output stream and continue --show-module-resolution show module resolution output during startup -? -h -help print this help message to the error stream --help print this help message to the output stream -X print help on extra options to the error stream --help-extra print help on extra options to the output stream -ea[:…|:] -enableassertions[:…|:] enable assertions with specified granularity -da[:…|:] -disableassertions[:…|:] disable assertions with specified granularity -esa | -enablesystemassertions enable system assertions -dsa | -disablesystemassertions disable system assertions -agentlib:[=] load native agent library , e.g. -agentlib:jdwp see also -agentlib:jdwp=help -agentpath:[=] load native agent library by full pathname -javaagent:[=] load Java programming language agent, see java.lang.instrument -splash: show splash screen with specified image HiDPI scaled images are automatically supported and used if available. The unscaled image filename, e.g. image.ext, should always be passed as the argument to the -splash option. The most appropriate scaled image provided will be picked up automatically. See the SplashScreen API documentation for more information @argument files one or more argument files containing options --disable-@files prevent further argument file expansion --enable-preview allow classes to depend on preview features of this releaseTo specify an argument for a long option, you can use –= or– .

I have gotten rid of the extra newlines with code based on this answer. However, some programs just freeze the shell if their help is too long (for instance, java.exe which outputs it’s help all at one time, vs. jar.exe which only outputs a small amount of its help).

I’ve found the issue(?), although I’m not sure why this happens.

If you put the error output after the standard output, the shell freezes when a program encounters an error (like, for instance, when you don’t have enough arguments). So, make your code like this:

string filePath = u.FindExecutableFromPath(command);

if (filePath != null)
{
    Process p = new Process
    {
        StartInfo = new ProcessStartInfo
        {
            FileName = filePath,
            Arguments = args != null ? String.Join(" ", args) : "",
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            UseShellExecute = false,
            CreateNoWindow = true
        }
    };

    p.Start();
    // Print the Error stream first, THEN the Standard stream second.
    Console.Error.Write(p.StandardError.ReadToEnd());
    Console.Write(p.StandardOutput.ReadToEnd());
    p.WaitForExit(2000);

} else
{
    Console.WriteLine(command + commandNotFoundError);
}

Another problem I found while testing:

You shouldn’t pass your program into Process.Start() as the full path, only the program name from your command; since the point of the test is to open programs from PATH.

1 Like

Well programs often, especially programs intended to be used in a shell, use synchronous blocking read and writes. Writes go into small buffers and when they are full the call blocks until the buffer clears.

You can then get freezes because of all kinds of permutations of blocking.

  1. For example suppose the program writes to its stdout but the buffer is full. It won’t proceed until you free the buffer by reading from it.
  2. You are reading from the programs stderr to forwards it to your shells stderr. You are blocked until data arrives or the the programs stderr is closed, for example by the process terminating.

With small amounts of data sometimes the buffering can hide these issues. But basically your shell is stuck writing stderr (but at its the reading part, p.StandardError.ReadToEnd()). But the program is stuck waiting for its stdout to clear. Swapping the order, just changes how it might get stuck.

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.