Архивы: по дате | по разделам | по авторам

Программное перенаправление ввода-вывода

АрхивПрограммазм (архив)
автор : Денис Колисниченко   25.09.2002

Программное управление вводом-выводом в linux

Всем нам известно как использовать перенаправление ввода/вывода из командной строки Linux. Напомню, что для перенаправления ввода/вывода используются символы >, <, >> и символ |. Символ > используется для перенаправления стандартного вывода в файл. Символ < используется для переназначения стандартного ввода команды. Например, при выполнении команды cat < file.txt в качестве стандартного ввода будет использован файл file.txt, а не клавиатура.

Символ >> используется для присоединения данных в конец файла (append) стандартного вывода команды. Символ | используется для перенаправления стандартного вывода одной программы на стандартный ввод другой. Например, ps -ax | grep httpd.

Более подробно о перенаправлении ввода/вывода вы можете прочитать в моей статье «Управление процессами», которую вы сможете найти на сервере Софтерры или на моем сайте.

Перенаправление ввода/вывода можно осуществить и программным путем, то есть без вмешательства пользователя. Наша программа сама перенаправит свой ввод/вывод в нужный файл или какой-нибудь программе.

Вызов system() порождает дочерний процесс, позволяя ему читать данные со стандартного ввода (stdin) и писать на стандартный вывод (stdout). Иногда нам нужно передать данные дочернему процессу или, наоборот, получить информацию от порожденного процесса. Другими словами мы хотим, чтобы дочерний процесс получал данные не со стандартного ввода, а от родительского процесса или/и выводил информацию не на стандартный вывод, а передавал ее процессу-предку. Ввод/вывод между процессами осуществляется с помощью системного вызова popen().

FILE * popen(const char * команда, const char * режим_доступа);

Первый параметр – это название программы, которую мы хотим запустить (дочерний процесс). Второй параметр определяет режим доступа. Установите значение «r», если вам нужно читать вывод дочернего процесса, если же вам нужно передать информацию на стандартный ввод порожденного процесса, установите значение «w». Вы не можете комбинировать сразу оба режима доступа, то есть нельзя установить режим «wr», позволяющий двусторонний обмен.

Вызов popen() возвращает указатель FILE* или пустой указатель NULL, если вызов не удался. Так же, как и при работе с обыкновенными файлами, вы должны закрыть канал вызовом pclose() после завершения операции ввода/вывода. Во время работы с потоком рекомендую использовать вызов fflush(), чтобы предотвратить задержки из-за буферизации.

Теперь несколько простых примеров. Предположим, что нам нужно вывести на стандартный вывод имена всех текстовых файлов, содержащихся в текущем каталоге. Это можно очень просто сделать с помощью вызова system():

system("ls *.txt");

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

// открываем поток
FILE *fp = popen("ls *.txt", "r");

// в цикле читаем имена всех текстовых файлов
while ((fname = fgets(...,fp);) != EOF) 
{
// обрабатываем полученную информацию - fname
}

// закрываем поток
pclose(fp);

Данный код в особых комментариях не нуждается. Сначала мы создаем поток для чтения (доступ «r») информации от порожденного процесс (ls *.txt). Затем в цикле while читаем имена файлов и обрабатываем. Чтение производим до тех пор, пока не будет достигнут конец файла. После окончания операции ввода/вывода закрываем поток вывозом pclose(fp);

Вот теперь мы готовы к тому, чтобы рассмотреть более серьезный пример. В этом примере мы будем передавать данные дочернему процессу. Задача такова: у нас есть две программы. Первая программа передает второй какую-нибудь информацию, вторая обрабатывает ее и выводит на стандартный вывод результат.

#include <STDIO.H>
#include <SYS wait.h>
#include <UNISTD.H>

int main()
{
char buff[1024]={0};
FILE * cp; // cp - child process – дочерний процесс
int status;
// Открываем канал для записи. Дочерний процесс – /usr/bin/child
cp=popen("/usr/bin/child", "w");
if (!cp)
{
printf("не могу открыть канал.\n");
exit(1)
}

printf("Введите информацию для передачи дочернему процессу" );
// читаем ввод пользователя
fgets(buff, sizeof (buff), stdin); 

// передаем данные дочернему процессу
fprintf(cp, "%s\n", buff);
// «выталкиваем» содержимое буфера в канал
fflush(cp);

// закрываем канал и проверяем состояние вызова pclose()
status=pclose(cp);
if (!WIFEXITED(status)) 
printf("ошибка при закрытии канала\n");
printf("Завершение работы родительского процесса\n"); return 0;
}

Теперь несколько комментариев. Мы используем вызов popen(), чтобы установить связь между родительским и дочерним процессами. Если мы не можем этого сделать по какой-либо причине, например, исполнимый файл дочернего процесса /usr/bin/child не существует, мы выводим сообщение об ошибке и завершаем работу программы. Потом мы читаем со стандартного ввода родительского процесса информацию для передачи дочернему процессу. С помощью вызова fprintf() мы передаем информацию по открытому каналу порожденному процессу. При этом вызов fprintf() используется так же, как и при работе с обыкновенными файлами. Во избежание задержек при передаче данных по каналу мы используем вызов fflush() для «выталкивания» данных из буфера в порожденный процесс. После окончания работы с каналом мы его закрываем и проверяем состояние вызова pclose(). При невозможности корректного закрытия канала мы выводим соответствующее предупреждение.

Программа child может выглядеть таким образом:

#include <STDIO.H>
int main()
{
char buff[1024]={0};
fgets(buff, sizeof (buff), stdin); 
printf("Прочитал со стандартного ввода: %s\n",buff);
printf("Завершение работы дочернего процесса\n");
return 0;
}

Как видите, ничего сверхъестественного в ней нет – программа просто читает данные со стандартного ввода и обрабатывает их: выводит на стандартный вывод.

На экране при запуске родительской программы (позаботьтесь о существовании программы /usr/bin/child) вы увидите примерно следующее:

Введите информацию для передачи дочернему процессу hello
Прочитал со стандартного ввода: hello
Завершение работы дочернего процесса
Завершение работы родительского процесса

На этом обзор программного управления вводом/выводом можно считать завершенным. Все свои вопросы и комментарии можете отправлять по адресу: dhsilabs@mail.ru.

© ООО "Компьютерра-Онлайн", 1997-2024
При цитировании и использовании любых материалов ссылка на "Компьютерру" обязательна.