Поиск

Перенаправление выходного потока

Несмотря на все преимущества, у функции system() есть также один существенный недостаток. Она не позволяет перенаправить выходной поток программы на дальнейшую обработку интерпретатору Perl. Чтобы устранить описанный недостаток, можно воспользоваться обходным маневром, как показано ниже на примере.

В этом примере выходные данные команды dir (или ls) перенаправляются командной оболочкой в файл outfile. Затем этот файл открывается и его содержимое помещается в массив @data. Таким образом, в этом массиве будут содержаться данные, полученные от команды dir.

Описанный метод слишком громоздкий и не вполне очевидный. Поэтому нет ничего удивительного в том, что в Perl предусмотрено более элегантное решение — использование оператора подстановки команд, или обратных кавычек. Любая команда в Perl, заключенная в обратные кавычки(' '), рассматривается как внешняя программа и запускается на выполнение по аналогии с функцией system(). При этом выходной поток внешней программы перехватывается и возвращается в вызывающую программу так же, как это происходит при вызове функций, например:

В этом примере данные, полученные в результате выполнения команды dir, помещаются в переменную $directory.

Как и в функции system(), внутри обратных кавычек можно пользоваться всеми доступными средствами командной оболочки: символ > вызывает перенаправление выходного потока, символ | выполняет конвейерную обработку и (в системе UNIX) символ s позволяет запустить программу в фоновом режиме. Только не забудьте, что при перенаправлении выходного потока программы или ее запуске в фоновом режиме в программу на Perl не возвращаются никакие данные.

В скалярном контексте оператор подстановки команд возвращает выходные данные программы в виде текстовой строки. Если в данных содержится несколько строк текста, они разделяются в строке специальным символом — разделителем записей. В контексте списка выходные данные программы построчно присваиваются переменным списка. При этом в конце каждой строки помещается разделитель записей. Вот пример:

Здесь в цикле foreach обрабатывается каждая строка, находящаяся в массиве @dir.

В Perl существует альтернативная форма записи оператора подстановки команд. Вместо обратных кавычек можно использовать оператор qx{>. Команду, которую нужно выполнить, следует поместить в фигурные скобки, как показано ниже:

Оператор qx позволяет повысить читабельность программы, если при записи внешней команды внутрь обратных кавычек следует поместить служебные символы, такие как прямые и обратные кавычки, косую черту и др. (Напомним, что перед служебными символами следует поставить обратную косую черту.) Вот пример.

А теперь перепишем этот оператор так:

Вместо фигурных скобок можно использовать произвольные символы, а также любые парные символы, такие как 0, () и [ ].

Как избежать обращения к командной оболочке

В некоторых случаях бывает трудно определить границы области действия интерпретатора Perl и командной оболочки. Давайте рассмотрим два примера. Для UNIX:

Или то же самое для DOS и Windows:

Как определить, к чему в первом примере относится переменная $НОМЕ? Является ли она переменной Perl, или переменной окружения командной оболочки? А во втором примере? Является ли конструкция Swindirl переменной окружения командного интерпретатора coimand.com, или это хэш языка Perl, за которым помещен знак процента?

Хуже всего то, что переменная $НОМЕ интерпретируется Perl. А это означает, что она является скаляром Perl, а не переменной окружения командной оболочки, как вы, вероятно, предполагали. Таким образом, внутри обратных кавычек переменные заменяются их значениями, точно так же, как это происходит и внутри двойных кавычек (" "). Однако данное правило не относится к хэшу — только к массивам и скалярам. Таким образом, во втором примере конструкция %windir% относится к переменной окружения командного интерпретатора command.com.

Чтобы избежать интерпретации переменных внутри обратных кавычек, поместите перед ними символ обратной косой черты, как показано в следующем примере:

А вот пример для DOS и Windows:

В этих примерах используется значение переменной окружения НОМЕ в UNIX и windir в DOS.

При использовании альтернативной формы записи оператора подстановки команд в конструкции qx{} следует заменить символ-ограничитель, как показано в следующем примере:

Или для Windows и DOS:

Конструкция qx'' распознается интерпретатором Perl и обрабатывается особым образом: внутри одинарных кавычек не выполняется замена переменных на их значение. Таким образом, в операторе подстановки команд появляется возможность использовать любые служебные символы, не помещая перед ними символ обратной косой.

Конвейерная обработка

Конвейерная обработка используется в системах UNIX и DOS/Windows для передачи данных между процессами. Она позволяет связать выходной поток одного процесса с входным потоком другого. Давайте рассмотрим следующий набор команд DOS, который с небольшими изменениями (команду dir нужно заменить на ls) будет работать и в UNIX:

Здесь выходные данные команды dir перенаправляются в файл outfile. Затем содержимое этого файла сортируется с помощью команды sort и записывается в новый файл newfile. И, наконец, содержимое файла newfile поэкранно выводится на терминал.

Конвейерная обработка позволяет выполнить те же самые действия, но без привлечения дополнительных временных файлов outfile и newfile:

В этом примере выходные данные команды dir подаются на вход команды sort, которая выполняет их сортировку. Затем отсортированные данные подаются на вход команды more для поэкранного отображения. При этом не требуется перенаправлять выходной поток (>) во временный файл, поскольку операционная система сделает все сама!

Выше был приведен пример конвейерной обработки данных, признаком которой служит вертикальная черта. Конвейеры активно используются в системе UNIX для обмена данными между многочисленными утилитами операционной системы. В DOS и Windows конвейерная обработка также поддерживается, но используется сравнительно редко и постепенно переходит в небытие по мере вытеснения текстовых утилит программами с графическим пользовательским интерфейсом.

Программы на Perl также могут участвовать в конвейерной обработке данных. Давайте создадим одну из таких программ, которая будет читать данные из стандартного входного потока, выполнять над ними определенные действия, а затем выводить в выходной поток, как показано в следующем примере:

В этом конвейере программа Totaler написана на Perl (листинг 11.1). Она выполняет подсчет количества файлов в каталоге и их суммарный размер. В системе UNIX замените команду dir /В на ls -1.

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

  • Строка 6. В цикле выполняется чтение строк из стандартного входного потока. При этом каждая строка присваивается переменной $_. При потоковой обработке дескриптору STDIN текущей программы соответствует дескриптор STDOUT предыдущей. Таким образом, в нашем примере из дескриптора STDIN считываются данные, полученные в результате выполнения команды dir /В.
  • Строки 9—13. Если встретился каталог, увеличим счетчик каталогов, находящийся в переменной Sdirs. При этом имя каталога распечатывается и цикл повторятся снова.
  • Строки 14—15. В противном случае размер файла прибавляется к содержимому переменной $sizes и имя файла распечатывается.
  • Строки 17—18. Накопленные статистические данные (количество файлов, каталогов и общий размер файлов) выводятся на печать.

Еще один способ конвейерной обработки данных заключается в том, что конвейер можно рассматривать как файл, информация в который может записываться, а затем счи-тываться. Открыть такой файл можно с помощью функции Perl open, как показано ниже:

В этом примере функция open открывает конвейер для чтения данных, полученных в результате выполнения цепочки команд dir /В | sort. Вертикальная черта, расположенная в цепочке команд крайней справа, говорит о том, что конвейер открывается для чтения. При выполнении функции open Perl запускает цепочку команд dir /В | sort и помещает полученные от команды sort данные во временный файл. Поэтому при чтении дескриптора RHANDLE эти данные попадут в программу на Perl для дальнейшей обработки.

А теперь рассмотрим еще один пример:

В этом случае функция open открывает конвейер для записи данных, которые подаются на вход команды more. Вертикальная черта, расположенная в цепочке команд крайней слева, говорит о том, что конвейер открывается для записи. Таким образом, все данные, выведенные в дескриптор WHANDLE, будут переданы в буфер программы more для поэкранного отображения. Кстати, вот вам один из способов, как можно заставить программу отображать данные постранично.

После того как будут закончены все операции чтения/записи с дескрипторами наподобие RHANDLE и WHANDLE, нужно обязательно закрыть эти дескрипторы и соответствующие им конвейеры. Тогда сможет корректно завершиться программа, запушенная функцией open. Если же после окончания работы с конвейером оставить дескриптор открытым, то внешняя программа останется в подвешенном состоянии после того, как программа на Perl завершит свою работу.

При закрытии конвейера с помощью функции close по коду возврата можно судить о том, как была выполнена конвейерная обработка. Поэтому всегда проверяйте, успешно или нет был закрыт конвейер, как показано ниже:

При изучении материала этого раздела у вас мог возникнуть вопрос; "Почему во время открытия конвейера с помощью функции open no коду возврата нельзя судить о том, успешно или нет была выполнена обработка данных?" Все дело в архитектуре системы UNIX. После того как интерпретатор Perl создал конвейер и выдал команду на его запуск, операционная система не обязательно сразу его запустит на выполнение. Поэтому остается надеяться, что если конвейер создан правильно и успешно запущен, то он корректно и завершит свою работу. После того как последняя программа в цепочке закончит свою работу, она должна вернуть соответствующий код возврата. Функция close считывает этот код, анализирует его, принимает решение о том, успешно или нет была выполнена обработка данных, и возвращает соответствующий код возврата.