Проверка стандартного ввода в С#

Я пишу небольшую утилиту командной строки, целью которой является синтаксический анализ вывода другой утилиты. Я хочу, чтобы это было invokable двумя способами:

c:\> myutility filewithoutput.txt

Или

c:\> otherutility -args | myutility

Итак, в основном, стандартный или аргумент файла. Моя первая попытка была такой:

TextReader reader;

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

Process(reader);

Аргумент файла отлично работает, и вывод результатов из утилиты в мою утилиту работает нормально, но если вы просто вызываете его нормально (без аргументов и без данных с каналами), он зависает. Или, скорее, он блокируется при ожидании чтения со стандартного входа.

Мой второй проект выглядел так:

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);

В то время как KeyAvailable исправляет проблему "без ввода", она выдает исключение, если вы пытаетесь выполнить вывод в 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

Исключение означает, что я использую Console.In.Peek, поэтому мой следующий проект таков:

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);

Однако у этой проблемы есть такая же проблема, что и при первой попытке: она блокирует поиск входов. Argh!

Есть ли что-то, что мне не хватает?

Sidenote: Я знаю о согласии аргумента "-", что означает "использовать стандартный ввод". Я буду использовать это, если нет другого пути. Но, конечно, должен быть какой-то способ определить, является ли стандартная консоль или нет!

Изменить: вот окончательная версия, которая, кажется, делает то, что мне нужно:

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);

Не большой поклонник использования исключений для потока, как этот, но... ах.

Ответы

Ответ 1

Я использую решение Pieter некоторое время, пока не понял, что он не работает в Mono. Mono не генерирует исключение при извлечении Console.KeyAvailable с помощью ввода в виде канала, поэтому подход не помогает.

Однако, с .NET 4.5, Console фактически предоставляет новое поле IsInputRedirected, которое делает это намного проще, удаляет безвестность и ненужный 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);

Ответ 2

Быстрый и грязный способ заключается в том, чтобы обернуть Console.KeyAvailable в try/catch, и если это произойдет, вы знаете, что вход перенаправляется из файла. Не очень необычно использовать try/catch для обнаружения состояния, когда вы не можете найти подходящий метод для проверки для вас.

Ответ 3

Похоже, вы должны использовать некоторые вызовы Windows API для определения этого. Ответ Hans Passant даже имеет вспомогательный класс, чтобы обернуть все это.