Как реализовать grep в C ++, чтобы он работал с конвейерами (stdin и т. Д.)? - PullRequest
1 голос
/ 20 апреля 2011

Я хочу реализовать grep для оболочки, которую я делаю в Windows (только для учебы).
Я знаю, что grep имеет следующий синтаксис:

grep pattern files

Так что я могу сделатьфункция вроде:

int grep(string stringToMatch, string fileName) // just one file
{
   // search file for stringToMatch
   // print line containing stringToMatch
}

Моя путаница в том, как grep должен работать, когда я использую трубу, подобную этой:

ls | grep someword

Я реализовал "ls", чтобы поместить весь выводвектор и вернуть его, так что я думаю, тогда мой grep должен искать вектор для результатов.Итак, как должна выглядеть правильная функция grep?Нужны ли мне 2 функции grep?

Заранее спасибо.

Ответы [ 6 ]

6 голосов
/ 20 апреля 2011

Вы хотите увидеть, сколько аргументов командной строки вы передали. Если есть только один, то вы предполагаете, что вы используете stdin вместо файла.

В C ++ это можно абстрагировать, используя ссылку на std::istream в вашей функции. Перед вызовом функции вы решаете (и создаете) std::ifstream, если это уместно, или используете std::cin в противном случае.

Таким образом, ваша функция становится:

int grep(string stringToMatch, std::istream& in) // just one file
{
   // search file for stringToMatch
   // print line containing stringToMatch
}

И вы можете использовать условное выражение (используя argc и argv в main) для выполнения:

grep(string, std::cin);

или

std::ifstream file(fileName.c_str());
grep(string, file);
4 голосов
/ 20 апреля 2011

Читайте о UNIX фильтрах или здесь

Фильтры Unix взаимодействуют на стандартном входе и стандартном выходе. То есть стандартный вывод первого процесса поступает на стандартный ввод второго процесса.

Стандартный ввод и вывод - это, по существу, двоичные / текстовые потоки

Этот метод может быть связан. Оболочка обычно является стороной, которая управляет - среда - запуск, мониторинг и выход процессов - взаимосвязи

Так 0. пользователь дает команду, например, Ls 1. оболочка находит команду, создает новый процесс, подключает стандартный вывод с терминала и стандартный вывод с терминала, 2. ждет выполнения программы 3. устанавливает среду с результатом подпроцесса

Если вы говорите, что у вас есть вывод ls в векторе, я боюсь, что вы не очень близки к программированию оболочки в истинном стиле

Если вы хотите создать оболочку без всех особенностей управления процессами, каналов, перенаправлений и прочего, наиболее полезным средством будут std :: istream и std :: ostream (или библиотека Boost IOStreams).

Очень очень простая (действительно очень глупая) версия grep может выглядеть так:

#include <iostream>
#include <string>

static bool is_match(const std::string& text, const std::string& pattern)
{
    // FIXME TODO use actual (posix?) regex, boost regex, regex++ or whatnot
    return std::string::npos != text.find(pattern);
}


int main(int argc, const char* argv[])
{
    switch(argc)
    {
        case 0: case 1:
            std::cerr << "specify the pattern" << std::endl;
            return 254;
        case 2:
            break;
        default:
            std::cerr << "not implemented" << std::endl;
            return 255;
    }
    const std::string pattern(argv[1]);

    std::string line;
    while (std::getline(std::cin, line))
    {
        if (is_match(line, argv[1]))
            std::cout << line << std::endl;
    }

    return 0;
}

Существуют лучшие примеры Например. здесь но, судя по вопросу, я подумал, что это был следующий информативный шаг по дороге;)

Также обратите внимание, что библиотека Boost IOstreams, похоже, содержит встроенную поддержку конвейеров

2 голосов
/ 20 апреля 2011

Ваша функция grep должна работать на FILE * (или эквиваленте C ++). Если вы получили имя файла, переданное в качестве аргумента, откройте этот файл. Если нет, прочитайте с stdin.

1 голос
/ 20 апреля 2011

Когда команда появляется в таком синтаксисе, как вы опубликовали, она должна считывать свой ввод из стандартного ввода. Итак, что вам нужно передать вашей функции, это не строка имени файла, а открытый дескриптор файла или FILE * к файлу, будь то резидентный файл файловой системы или стандартный ввод.

Что-то вроде:

FILE *f;

if (argc == 3)
  f = fopen(argv[2], "r");
else
  f = stdin;

grep(argv[1], f);

Обратите внимание, что если вы выполните "ls | grep bar foo", grep проигнорирует вывод ls и будет соответствовать "bar" в файле "foo". Таким образом, приведенный выше код отражает (с множеством недостатков, которые встроены, но не полностью, поскольку grep может сопоставлять несколько файлов), поведение grep.

1 голос
/ 20 апреля 2011

Возможно, вы захотите использовать некоторую библиотеку, такую ​​как Boost Regex и вычислить результат шаблона, введенного в вашу оболочку.

В случае с pipe это особенность оболочки ине grep.Вы можете проверить Boost Interprocess и Boost Asio библиотеки для его реализации.Boost Asio поддерживает множество механизмов межпроцессного взаимодействия POSIX.

0 голосов
/ 20 апреля 2011

открыть заново стандартный , когда пользователь установил второй аргумент.

...