Проверка стандартного ввода в C # - PullRequest
14 голосов
/ 18 октября 2010

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

c:\> myutility filewithoutput.txt

Или,

c:\> otherutility -args | myutility

Так, в принципе, стандартный аргумент in или file. Моя первая попытка это выглядела так:

TextReader reader;

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

Process(reader);

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

Мой второй черновик выглядел так:

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 исправляет проблему «без ввода», оно выдает исключение, если вы пытаетесь передать данные> _ <</p>

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

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

Ответы [ 3 ]

10 голосов
/ 18 октября 2010

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

5 голосов
/ 13 декабря 2015

Я некоторое время использовал решение Питера, пока не понял, что оно не работает в 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);
1 голос
/ 18 октября 2010

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

...