Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can not redirect shell command 'read' #89

Open
my522cn opened this issue Sep 26, 2021 · 5 comments
Open

Can not redirect shell command 'read' #89

my522cn opened this issue Sep 26, 2021 · 5 comments
Labels

Comments

@my522cn
Copy link

my522cn commented Sep 26, 2021

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
@madelson
Copy link
Owner

@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)...

@my522cn
Copy link
Author

my522cn commented Nov 2, 2021

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

@madelson
Copy link
Owner

madelson commented Nov 2, 2021

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?

@my522cn
Copy link
Author

my522cn commented Nov 4, 2021

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

@madelson
Copy link
Owner

madelson commented Jan 6, 2023

The following worked for me:

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants