Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Checking Standard Input in C#

I'm writing a small command line utility whose purpose is to parse the output of another utility. I want it to be invokable in two ways:

c:\> myutility filewithoutput.txt

Or,

c:\> otherutility -args | myutility

So, basically, standard in or a file argument. My first attempt at this looked like this:

TextReader reader;

if (args.Length > 1) {
    reader = new StreamReader(new FileStream(args[1], FileMode.Open));
} else {
    reader = Console.In;
}

Process(reader);

The file argument works fine, and piping the output from the utility to my utility works fine, but if you just invoke it normally (no arguments and no piped data), it hangs. Or, rather, it blocks on waiting to read from standard in.

My second draft looked like this:

TextReader reader;

if (args.Length > 1) {
    reader = new StreamReader(new FileStream(args[1], FileMode.Open));
} else {
    if(Console.KeyAvailable) {
        reader = Console.In;
    } else {
        Console.WriteLine("Error, need data");
        return;
    }
}

Process(reader);

While KeyAvailable fixes the "no input" problem, it throws an exception if you try to pipe in data >_<

Unhandled Exception: System.InvalidOperationException: Cannot see if a key
has been pressed when either application does not have a console or when
console input has been redirected from a file. Try Console.In.Peek.

at System.Console.get_KeyAvailable()
at MyUtility.Program.Main(String[] args) in Program.cs:line 39

The exception suggests I use Console.In.Peek, so my next draft is as such:

TextReader reader;

if (args.Length > 1) {
    reader = new StreamReader(new FileStream(args[1], FileMode.Open));
} else {
    if(Console.In.Peek() != 0) {
        reader = Console.In;
    } else {
        Console.WriteLine("Error, need data");
        return;
    }
}

Process(reader);

However, this has the same problem as the first try: It blocks, looking for input. Argh!

Is there something I'm missing?

Sidenote: I am aware of the convention of the argument "-" meaning "use standard input". I will use that if there's no other way. But, surely there's got to be some way of detecting if standard in is the console or not!

Edit: Here's the final version that seems to do what I need:

TextReader reader;

if (args.Length > 1) {
    reader = new StreamReader(new FileStream(args[1], FileMode.Open));
} else {
    try {
        bool tmp = Console.KeyAvailable;
        Console.WriteLine("Error, need data");
        return;
    } catch(InvalidOperationException) {
        reader = Console.In;
    }
}

Process(reader);

Not a big fan of using Exceptions for flow like that, but... eh.

like image 386
Mike Caron Avatar asked Oct 18 '10 17:10

Mike Caron


People also ask

What is the standard input in C?

The stdin is the short form of the “standard input”, in C programming the term “stdin” is used for the inputs which are taken from the keyboard either by the user or from a file. The “stdin” is also known as the pointer because the developers access the data from the users or files and can perform an action on them.

What is stdin and stdout in C?

In computer programming, standard streams are interconnected input and output communication channels between a computer program and its environment when it begins execution. The three input/output (I/O) connections are called standard input (stdin), standard output (stdout) and standard error (stderr).

Why stdin is used in C?

fflush() is typically used for output stream only. Its purpose is to clear (or flush) the output buffer and move the buffered data to console (in case of stdout) or disk (in case of file output stream). Below is its syntax.


2 Answers

The quick and dirty way is to wrap Console.KeyAvailable in a try/catch and if that throws, you know that input is redirected from a file. It's not very unusual to use try/catch to detect a state when you cannot find an appropriate method to do the checking for you.

like image 95
Pieter van Ginkel Avatar answered Oct 11 '22 12:10

Pieter van Ginkel


I've been using Pieter's solution for a while until I realised it doesn't work in Mono. Mono doesn't throw an exception when retrieving Console.KeyAvailable with piped input, so that approach doesn't help.

However, as of .NET 4.5, Console actually provides a new field IsInputRedirected which makes this a lot simpler, removes the obscurity and the unnecessary try/catch:

TextReader reader;

if (args.Length > 1) {
    reader = new StreamReader(new FileStream(args[1], FileMode.Open));
} else {
    if (Console.IsInputRedirected) {
        reader = Console.In;
    } else {
        Console.WriteLine("Error, need data");
        return;
    }
}

Process(reader);
like image 25
Martin Ender Avatar answered Oct 11 '22 13:10

Martin Ender