MedallionShell icon indicating copy to clipboard operation
MedallionShell copied to clipboard

Can not redirect shell command 'read'

Open my522cn opened this issue 4 years ago • 5 comments

I have a shell script on Linux, and I use MesallionShell to redirect it, but can not print read output on screen.

#!/bin/bash 
echo ""
echo ""
echo ""
echo ""
echo ""
echo "I'm #1"
read -t 3 -p "Ready for test press any key or wait for 3 s" k
echo "I'm #2"
read -t 3 -p "Ready for test press any key or wait for 3 s" k
echo "I'm #3"
read -t 3 -p "Ready for test press any key or wait for 3 s" k
echo "I'm #4"
read -t 3 -p "Ready for test press any key or wait for 3 s" k
RESULTFILE=./test.result
echo "PASS">${RESULTFILE}
ls
var command = Command.Run(path, args, opt => opt.WorkingDirectory(workingdirectory));
command.RedirectTo(Console.OpenStandardOutput());
command.RedirectFrom(Console.OpenStandardInput());
command.RedirectStandardErrorTo(Console.OpenStandardError());
await command.Task;

When I run the script directly: demo2

When I use MesallionShell call the script: demo1

So how to print the message in read -p "message"

read -t 3 -p "Ready for test press any key or wait for 3 s" k

my522cn avatar Sep 26 '21 00:09 my522cn

@my522cn a few thoughts:

  1. Can you post code which shows how you set the values for path and args?
  2. Rather than awaiting the original command's task, you should chain together the sequence of redirections and await the result of the chain: var command = Command.Run(...).RedirectTo(...).RedirectStandardErrorTo(...).RedirectFrom(...);
  3. I wonder if you're running into internal buffering within standard out / standard error? For example, you could try the following code to redirect:
using var autoFlushWriter = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
...RedirectTo(autoFlushWriter)...

madelson avatar Sep 26 '21 21:09 madelson

hi, path content is the shell script what I shared. args is empty string.

my522cn avatar Nov 02 '21 02:11 my522cn

Probably args should be an empty array (no arguments) unless you mean to pass the empty string as an argument.

Did you try steps 2 and 3?

madelson avatar Nov 02 '21 11:11 madelson

Probably args should be an empty array (no arguments) unless you mean to pass the empty string as an argument.

Did you try steps 2 and 3?

No mater args is an enpty string or array, both the same. and i have tried step 2(flow.Run5) and 3(flow.Run6).

Here's the full demo code. (dotnet 6)

using CliWrap;
using CliWrap.EventStream;
using Medallion.Shell;
using System.Diagnostics;
using System.Runtime.InteropServices;
using CliWrap.Buffered;
using System.Text;

Console.WriteLine("Hello, World!");
string scriptfile;

while (true)
{
    scriptfile = "try.bat";
    string bat = @"@echo off
@REM echo.
echo This batch program
echo formats and checks
pause
echo new disks
@REM echo.

set /p a=a=
echo formats and checks
pause
set /p b=b=
echo %a%   %b%
exit 0
";

    if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
    {
        scriptfile = "try.sh";
        bat = @"#!/bin/bash 
echo """"
echo """"
echo """"
echo """"
echo """"
echo ""I'm #1""
read -t 3 -p ""Ready for test press any key or wait for 3 s"" k
echo ""I'm #2""
read -t 3 -p ""Ready for test press any key or wait for 3 s"" k
echo ""I'm #3""
read -t 3 -p ""Ready for test press any key or wait for 3 s"" k
echo ""I'm #4""
read -t 3 -p ""Ready for test press any key or wait for 3 s"" k
RESULTFILE =./ test.result
echo ""PASS"" >${ RESULTFILE}
ls
";
    }

    File.WriteAllText(scriptfile, bat);

    Task.Run(async () =>
    {
        using (var flow = new TestFlow())
        {
            //await flow.Run();
            //await flow.Run1();
            //await flow.Run2();
            //await flow.Run3();
            //await flow.Run4();
            //await flow.Run5(scriptfile);
            await flow.Run6(scriptfile);
        }

        Console.WriteLine("\nNEXT");
    }).Wait();
}

internal class TestFlow : IDisposable
{
    ~TestFlow()
    {
        Dispose();
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }

    internal async Task Run(string scriptfile)
    {
        var command = Medallion.Shell.Command.Run(scriptfile);
        command.RedirectFrom(Console.OpenStandardInput());
        command.RedirectTo(Console.OpenStandardOutput());
        //command.RedirectStandardErrorTo(Console.OpenStandardError()); 
        var result = await command.Task;
        Console.SetIn(new StreamReader(Console.OpenStandardInput()));
        command.Kill();
        //Console.WriteLine(command.Task.Dispose());
        Console.Write("Enter 1:");
        var str = Console.ReadLine();                 // can not access input
        Console.WriteLine(">" + str);
    }

    internal async Task Run1(string scriptfile)
    {
        var command = Medallion.Shell.Command.Run(scriptfile)
        .RedirectTo(Console.OpenStandardOutput())
        .RedirectFrom(Console.OpenStandardInput())
        //.RedirectFrom("EXIT")
        .RedirectStandardErrorTo(Console.OpenStandardError());
        //command.StandardInput.PipeFromAsync(Console.OpenStandardInput(), true);
        //command.RedirectTo(Console.OpenStandardOutput());
        //command.StandardInput.AutoFlush = true;
        //command.StandardInput.PipeFromAsync(Console.In, true);
        var result = await command.Task;              // can not quit
        Console.SetIn(Console.In);
        Console.Write("Enter 1:");
        var str = Console.ReadLine();
        Console.WriteLine(">" + str);
    }

    internal async Task Run2(string scriptfile)
    {

        var stdOutBuffer = Console.OpenStandardOutput();
        var stdErrBuffer = Console.OpenStandardError();
        var stdInpBuffer = Console.OpenStandardInput();

        var result = await Cli.Wrap(scriptfile)
            .WithStandardOutputPipe(PipeTarget.ToStream(stdOutBuffer))
            .WithStandardErrorPipe(PipeTarget.ToStream(stdErrBuffer))
            .WithStandardInputPipe(PipeSource.FromStream(stdInpBuffer))
            .ExecuteAsync();



        Console.Write("Enter 1:");
        var str = Console.ReadLine();          // can not access input
        Console.WriteLine(">" + str);
    }

    internal async Task Run3(string scriptfile)
    {

        var stdOutBuffer = Console.OpenStandardOutput();
        var stdErrBuffer = Console.OpenStandardError();
        var stdInpBuffer = Console.OpenStandardInput();

        var wrap = Cli.Wrap(scriptfile)
            .WithStandardOutputPipe(PipeTarget.ToStream(stdOutBuffer))
            .WithStandardErrorPipe(PipeTarget.ToStream(stdErrBuffer))
            .WithStandardInputPipe(PipeSource.FromStream(stdInpBuffer));
        var result = await wrap.ExecuteBufferedAsync();

        wrap.WithStandardOutputPipe(PipeTarget.Null);
        wrap.WithStandardErrorPipe(PipeTarget.Null);
        wrap.WithStandardInputPipe(PipeSource.Null);


        Console.Write("Enter 1:");
        var str = Console.ReadLine();          // can not access input
        Console.WriteLine(">" + str);
    }

    internal async Task Run4(string scriptfile)
    {
        var stdOutBuffer = Console.OpenStandardOutput();
        var stdErrBuffer = Console.OpenStandardError();
        var stdInpBuffer = Console.OpenStandardInput();

        var wrap = Cli.Wrap(scriptfile)
            .WithStandardOutputPipe(PipeTarget.ToStream(stdOutBuffer))
            .WithStandardErrorPipe(PipeTarget.ToStream(stdErrBuffer))
            .WithStandardInputPipe(PipeSource.FromStream(stdInpBuffer));
        var result = await wrap.ExecuteBufferedAsync();

        Console.WriteLine(result.StandardOutput);
        Console.WriteLine("Enter 1:");
        var str = Console.ReadLine();          // can not access input
        Console.WriteLine(">" + str);
    }

    internal async Task Run5(string scriptfile)
    {
        var command = Medallion.Shell.Command.Run(scriptfile)
        .RedirectTo(Console.OpenStandardOutput())
        .RedirectStandardErrorTo(Console.OpenStandardError())
        .RedirectFrom(Console.OpenStandardInput());
        var result = await command.Task;   
        Console.SetIn(Console.In);
        Console.Write("Enter 1:");
        var str = Console.ReadLine();
        Console.WriteLine(">" + str);

        while (true)
        {
            Console.Write("<");
            Console.WriteLine(">" + Console.ReadLine());
        }
    }

    internal async Task Run6(string scriptfile)
    {
        using var autoFlushWriter = new StreamWriter(Console.OpenStandardOutput()) { AutoFlush = true };
        var command = Medallion.Shell.Command.Run(scriptfile)
        .RedirectTo(autoFlushWriter)
        .RedirectStandardErrorTo(Console.OpenStandardError())
        .RedirectFrom(Console.OpenStandardInput());
        var result = await command.Task;  
        Console.SetIn(Console.In);
        Console.Write("Enter 1:");
        var str = Console.ReadLine();
        Console.WriteLine(">" + str);

        while (true)
        {
            Console.Write("<");
            Console.WriteLine(">" + Console.ReadLine());
        }
    }


}

And one more thing, when I redirect stdin to current console, even the process exited, console.readline cannot access any inputs immediately, pls check flow.Run5

my522cn avatar Nov 04 '21 07:11 my522cn

The following worked for me:

string line;
while (!command.Task.IsCompleted 
	&& (line = Console.ReadLine()) != null)
{
	command.StandardInput.WriteLine(line);
}

madelson avatar Jan 06 '23 01:01 madelson