C# Console Receive Input with Pipe

C# Console receive input with pipe

You need to use Console.Read() and Console.ReadLine() as if you were reading user input. Pipes replace user input transparently. You can't use both easily (although I'm sure it's quite possible...).

Edit:

A simple cat style program:

class Program
{
static void Main(string[] args)
{
string s;
while ((s = Console.ReadLine()) != null)
{
Console.WriteLine(s);
}

}
}

And when run, as expected, the output:


C:\...\ConsoleApplication1\bin\Debug>echo "Foo bar baz" | ConsoleApplication1.exe
"Foo bar baz"

C:\...\ConsoleApplication1\bin\Debug>

c# console: how can i read piped input from the command line

If you want to pipe from outputsomething.exe to dostuffwithinput.exe it should be

outputsomething.exe|dostuffwithinput.exe some args

Pipe Data From Command Line into C# Console App

Piped outputs from another command are not passed to your application as command line arguments (i.e. args[] in Main()). Rather, they need to be read in by your application from the standard input (stdin). You need to use Console.Read() or Console.ReadLine() for that.

C# Console receive input with pipe

You need to use Console.Read() and Console.ReadLine() as if you were reading user input. Pipes replace user input transparently. You can't use both easily (although I'm sure it's quite possible...).

Edit:

A simple cat style program:

class Program
{
static void Main(string[] args)
{
string s;
while ((s = Console.ReadLine()) != null)
{
Console.WriteLine(s);
}

}
}

And when run, as expected, the output:


C:\...\ConsoleApplication1\bin\Debug>echo "Foo bar baz" | ConsoleApplication1.exe
"Foo bar baz"

C:\...\ConsoleApplication1\bin\Debug>

Writing to two standard input pipes from C#

Based on what is written here and on a good night of sleep (where I dreamt that I could use Stream.CopyAsync), this is the skeleton of the solution:

string pathToFFmpeg = @"C:\ffmpeg\bin\ffmpeg.exe";

string[] inputs = new[] { "video.m4v", "audio.mp3" };

string output = "output2.mp4";

var npsss = new NamedPipeServerStream[inputs.Length];
var fss = new FileStream[inputs.Length];

try
{
for (int i = 0; i < fss.Length; i++)
{
fss[i] = File.OpenRead(inputs[i]);
}

// We use Guid for pipeNames
var pipeNames = Array.ConvertAll(inputs, x => Guid.NewGuid().ToString("N"));

for (int i = 0; i < npsss.Length; i++)
{
npsss[i] = new NamedPipeServerStream(pipeNames[i], PipeDirection.Out, 1, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
}

string pipeNamesFFmpeg = string.Join(" ", pipeNames.Select(x => $@"-i \\.\pipe\{x}"));

using (var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = pathToFFmpeg,
Arguments = $@"-loglevel debug -y {pipeNamesFFmpeg} -c:v copy -c:a copy ""{output}""",
UseShellExecute = false,
}
})
{
Console.WriteLine($"FFMpeg path: {pathToFFmpeg}");
Console.WriteLine($"Arguments: {proc.StartInfo.Arguments}");

proc.EnableRaisingEvents = false;
proc.Start();

var tasks = new Task[npsss.Length];

for (int i = 0; i < npsss.Length; i++)
{
var pipe = npsss[i];
var fs = fss[i];

pipe.WaitForConnection();

tasks[i] = fs.CopyToAsync(pipe)
// .ContinueWith(_ => pipe.FlushAsync()) // Flush does nothing on Pipes
.ContinueWith(x => {
pipe.WaitForPipeDrain();
pipe.Disconnect();
});
}

Task.WaitAll(tasks);

proc.WaitForExit();
}
}
finally
{
foreach (var fs in fss)
{
fs?.Dispose();
}

foreach (var npss in npsss)
{
npss?.Dispose();
}
}

There are various attention points:

  • Not all formats are compatible with pipes. For example many .mp4 aren't, because they have their moov atom towards the end of the file, but ffmpeg needs it immediately, and pipes aren't searchable (ffmpeg can't go to the end of the pipe, read the moov atom and then go to the beginning of the pipe). See here for example

  • I receive an error at the end of the streaming. The file seems to be correct. I don't know why. Some other persons signaled it but I haven't seen any explanation

    \.\pipe\55afc0c8e95f4a4c9cec5ae492bc518a: Invalid argument
    \.\pipe\92205c79c26a410aa46b9b35eb3bbff6: Invalid argument

  • I don't normally use Task and Async, so I'm not 100% sure if what I wrote is correct. This code doesn't work for example:

    tasks[i] = pipe.WaitForConnectionAsync().ContinueWith(x => fs.CopyToAsync(pipe, 4096)).ContinueWith(...);

    Mmmmh perhaps the last can be solved:

    tasks[i] = ConnectAndCopyToPipe(fs, pipe);

    and then

    public static async Task ConnectAndCopyToPipe(FileStream fs, NamedPipeServerStream pipe)
    {
    await pipe.WaitForConnectionAsync();
    await fs.CopyToAsync(pipe);
    // await fs.FlushAsync(); // Does nothing
    pipe.WaitForPipeDrain();
    pipe.Disconnect();
    }

Pass text file content to Console.ReadLine() instead of typing

Thanks @Marc Gravell and @Olivier Rogier.

Answer is to use pipes.

myconsoleapp main.cs

Public static void main(args){
String input = Console.ReadLine();
Console.WriteLine(input);
}

input.txt file

Hello world

Command-line

 myconsoleapp.exe < input.txt

output

Hello world

Reference:
ss64.com/nt/syntax-redirection.html

How-to: Redirection
command > filename Redirect command output to a file

command >> filename APPEND into a file

command < filename Type a text file and pass the text to command

commandA | commandB Pipe the output from commandA into commandB

commandA & commandB Run commandA and then run commandB
commandA && commandB Run commandA, if it succeeds then run commandB
commandA || commandB Run commandA, if it fails then run commandB

commandA && commandB || commandC
If commandA succeeds run commandB, if commandA fails run commandC
( Note that if commandB fails, that will also trigger running commandC )


Related Topics



Leave a reply



Submit