Создание серверных приложений на языке PERL

Управление процессами


Использование функций system u ехес

Когда вы из командной строки shell задаєте выполнение какой-либо команды, он обычно создает новый процесе. Зтот новый процесе становится порожденным процессом shell, выполняется независимо, но в координации с последним.

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

Самий простой способ запуска нового процесса — использовать для зтого функцию system. В простейшей форме зта функция передает в совершенно новый shell /bin/sh одну строку, которая будет выполняться как команда. По выполнении команды функция system возвращает код завершення данной команды (если все было нормально — зто, как правило, 0). Вот пример того, как Perl-программа выполняет команду date c помощью shell*:

system("date");

Здесь мы не проверяем возвращаемое значение, но неудачный исход выполнения команд н date вряд ли возможен.

Куда идет результат зтой команди? Откуда поступают исходные данные, если они нужны команде? Хорошие вопросы, и ответы на них позволят узнать, чем отличаются различные способы создания процессов.

* В данном случае shell фактически не используется, поскольку Рсгі сам выполняет операций shell, если командная строка достаточно проста, как в данном случае.

Три стандартних файла для функции system (стандартний ввод, стандартний внвод и стандартний вивод ошибок) наследуются от Perl-процесса. Таким образом, результат выполнения команди dale в приведенном више примере направляется туда же, куда поступает результат выполнения функции print stdout — скореє всего, на дисплей визвавшего ее пользователя. Поскольку ви запускаєте shell, то можете переадресовать стандартний вивод, пользуясь обычными для /Ып/sh операциями переадресации. Например, чтобы направить результати работи команди date в файл right_now, нужно сделать что-то вроде зтого:

system("date >right_now") && die "cannot create right_now";

На зтот раз ми не только посилаєм результат команди date в файл, внполняя переадресацию в shell, но и проверяем статус возврата. Если статус возврата — значение "истина" (не нуль), зто значит, что с командой shell что-то произошло, и функция die внполнит свою миссию. Данное правило обратно обичним правилам выполнения операций в Perl: ненулевое возвра-щаемое значение операций system, как правило, указивает на какую-то ошибку.


Аргументом функции system может бить все, что пригодно для передачи в /Ып/sh, позтому можно задавать сразу несколько команд, разделяя их точками с запятой или символами новой строки. Процесси, после которих указан символ &, запускаются, но программа не ждет их завершення, т.е. в данном случае все происходит аналогично тому, как если би ви ввели в shell строку, которая заканчивается символом &.



Вот пример задания команд date и who в shell с передачей результатов в файл, заданний Perl-переменной. Все зто вьшолняется в фоновом режиме, чтобн для продолжения выполнения Perl-сценария не нужно било ждать завершення данного процесса.

$where = "who_out.".++$і; t получить новое имя файла system "(date; who) >$where &";

В зтом случае функция system возвращает код выхода shell и показывает таким образом, успешно ли бил запущен фоновий процесе, но не сообщает, били ли успешно выполнени команди date и who. В зтой заключенной в двойные кавички строке производится интерполяция переменних, позтому переменная $ where заменяется своим значением (зто делает Perl, а не shell). Если би ви хотели обратиться к переменной shell с именем $where, вам нужно било би поставить перед знаком доллара обратную косую или использовать строку в одинарних кавнчках.

Помимо стандартних дескрипторов файлов, порожденний процесе на-следует от родительского процесса много других вещей. Зто текущее значение, заданное командой umask, текущий каталог й, конечно, идентификатор пользователя.

Кроме того, порожденний процесе наследует все переменнне среды. Зти переменные обычно изменяются командой csh setenv или же соответствующим присваиванием и команд ой export shell (/bin/sh). Переменные среды исполь-зуются многими утилитами, включая сам shell, для изменения порядка работы зтих утилит и управления йми.

В Perl предусмотрена возможность проверки и изменения текущих пере-менных среды посредством специального хеша, который называется %env. Каждый ключ зтого хеша соответствует имени переменной среды, а соответ-ствующее значение — значенню переменной. Содержимое данного хеша отражает параметры среды, переданные Perl родительским shell; изменение хеша изменяет параметри среды, которую использует Perl и порожденные им процессы, но не среды, используемой родительскими процессами.



Вот простая программа, которая работает, как printenv:

foreach $key (sort keys %ENV) ( print "$key = $ENV($key)\n";

> . ,

Обратите внимание: знак равенства здесь — зто не символ операции присваивания, а просто текстовый символ, с помощью которого функция print выдает сообщения вида TERM=xterm или U3ER=merlyn.

Вот фрагмент программы, с помощью которого значение переменной path изменяется таким образом, чтобы поиск команды grep, запущенной функцией system, производился только в "обычных" местах:

$oldPATH = $ENV{ "PATH"); # сохранить предыдущий путь $ENV("PATH") = "/bin:/usr/bin:/usr/ucb"; # ввести известньй путь systemC'grep fred bedrock >output") ; # запустить команду $ENV{"PATH"} = $oldPATH; # восстановить предьшуший путь

Как много текста придется набирать! Гораздо быстрее будет просто установить локальнеє значение для зтого злемента хеша.

Несмотря на наличие некоторых недостатков, операция local может делать одну вещь, которая не под силу операции ту: она способна присваи-вать временное значение одному злементу массива или хеша.

{

local $ENV{"PATH"> " "/bin:/usr/bin:/usr/ucb"; ! systemC'grep fred bedrock >output");

>

Функция system может принимать не один аргумент, а список аргумен-тов. В зтом случае Perl не передает список аргументов в shell, а рассматривает первый аргумент как подлежащую выполнению команду (при необходимости производится ее поиск согласно переменной path), а остальные аргумен-ты — как аргументи команды без обычной для shell интерпретации. Другими словами, вам не нужно заключать в кавычки пробельные символы и беспо-коиться об аргументах, которые содержат угловые скобки, потому что все зто — просто символы, передаваемые в программу. Таким образом, следую-щие две команды зквивалентны:

system "grep 'fred flintstone' buffaloes"; # с использованием shell system "grep","fred flintstone","buffaloes"; # без использования shell



Применение в функции system списка, а не одной строки, зкономит также один процесе shell, позтому поступайте так при любой возможности. (Если форма функции system c одним аргументом достаточно проста, Perl сам оптимизирует код, полностью убирая вызов shell и обращаясь к соответ-ствующей программе непосредственно, как если бы вы использовали вызов функции с несколькими аргументами.)

Вот еще один пример зквивалентных форм:

Ocfiles = ("fred.c","barney.c"); # что компилировать Soptions = ("-DHARD","-DGRANITE"); # опции system "cc -o slate Soptions Bcfiles"; # c shell system "cc","-o","slate",goptions,Scfiles"; # без shell



Использование обратных кавычек



Еще один способ запуска процесса — заключить командную строку для /Ып/sh в обратные кавычки. Как и в shell, зтот механизм запускает команду и ожидает ее завершення, получая данные со стандартного вывода по мере их поступления:

$now = "the time is now".'date'; # получает текст и дату

Значение переменной $now теперь представляет собой текст the time is now и результат выполнения команди date(l) (включая конечний символ новой строки):

the time is now Fri Aug 13 23:59:59 PDT 1996

Если взятая в обратные кавычки команда используется не в скалярном, а в списочном контексте, то возвращается список строкових значений, каждое из которых представляет собой строку (оканчивающуюся символом новой строки*) из результата выполнения команды. В примере с командой date у нас был бы всего один злемент, потому что она видала всего одну строку текста. Результат работы команды who выглядит так:

merlyn tty42 Dec 7 19:41 fred ttylA Aug 31 07:02 barney ttylF Sep 1 09:22

Вот как можно получить зтот результат в списочном контексте:

foreach $_ ('who') ( # один раз для каждой строки текста из who <$who,$where,$when) = /(\S+)\s+(\S+)\s+(.*)/;

print "$who on $where at $when\n";

}

* Или символом, которьш у вас занесен в переменную $/.



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

Стандартний ввод и стандартний вывод ошибок команди, взятой в обратные кавички, наследуются от Perl-процесса*. Зто значит, что обычно стандартний вывод таких команд ви можете получить как значение строки, заключенной в обратные кавички. Одна из распространенных операций — обьединение стандартного внвода ошибок со стандартним виводом, чтобы команда в обратннх кавычках "подбирала" их оба. Для зтого используется конструкция shell 2>&1:

die "rm spoke!" if 'rm fred 2>&1';

Здесь Perl-процесе завершается, если rm посылает какое-нибудь сообще-ние — либо на стандартний вивод, либо на стандартний вивод ошибок, потому что результат больше не будет пустой строкой (пустая строка соот-ветствовала би значенню "ложь").



Использование процессов как дескрипторов файлов



Следующий способ запуска процесса — создание процесса, которий вигля-дит как дескриптор файла (аналогично библиотечной подпрограмме рореп(3), если ви с ней знакомы). Мы можем создать для процесса дескриптор файла, которий либо получает результат работы процесса, либо подает в него входные данные**. Ниже приведен пример создания дескриптора файла для процесса who(l). Поскольку зтот процесе вьщает результат, которий мы хотим прочитать, мы создаем дескриптор файла, открытый для чтения:

open(WHOPROC, "who 1"); # открнть who для чтения

Обратите внимание на вертикальную черту справа от who. Зта черта информирует Perl о том, что данная операция open относится не к имени файла, а к командо, которую необходимо запустить. Поскольку черта стоит справа от команди, данннй дескриптор файла открнвается для чтения. Зто означает, что предполагается прием данных со стандартного внвода команди who. (Стандартний ввод и стандартний вывод ошибок продолжают исполь-зоваться совместно с Perl-процессом.) Для остальной части программы дескриптор whoproc — зто просто дескриптор файла, которий открыт для



* На самом деле все не так просто. См. соответствующий ответ в разделе 8 сборника часто задаваемых вопросов по Perl ("Как перехватить stderr из внешней команды?"). Если у вас Perl версии 5.004, зтот сборник распространяется как обычная man-страница — в данном случас per/faq8( 1).

** Ho не одновременно. Примеры двунаправленной связи приведены в главе 6 книги Programming Perl и на man-странице рег1ірс(1).

чтения, что означает возможность вьшолнения над файлом всех обычных операций ввода-вывода. Вот как можно прочитать данные из команды who в массив:

@whosaid = <WHOPROC>;

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

open(LPR,"|Ipr -Psiatewriter") ;

print LPR Srockreport;

close(LPR) ;

В атом случае после открытия lpr мы записываем в него данные и закриваєм данный файл. Открьггие процесса с дескриптором файла позволяет выполнять команду параллельно с Perl-программой. Задание для дескриптора файла команды close заставляет Perl-программу ожидать завершения процесса. Если дескриптор не будет закрыт, процесе может продолжаться даже после завершения Perl-программы.

Открытие процесса для записи предполагает, что стандартний ввод команды будет получен из дескриптора файла. Стандартний вывод и стандартний вивод ошибок используются зтим процессом совместно с Perl. Как и прежде, ви можете использовать переадресацию ввода-вивода в етиле /bin/sh. Вот как в нашем последнем примере можно отбрасивать сообщения об ошибках команди Ipr.

open(LPR,"|Ipr -Psiatewriter >/dev/null 2>&1");

С помощью операций >/dev/null обеспечивается отбраснвание стандартного вивода путем переадресации его на нулевое устройство. Операция 2>&1 обеспечивает передачу стандартного вивода ошибок туда, куда направляется стандартний вывод, позтому сообщения об ошибках также отбрасываются.

Можно даже обьединить все зти фрагменти программи и в результате получить отчет обо всех зарегистрированных пользователях, кроме Фреда:



open (WHO,"who[ ") ;

open (LPR,"|Ipr - Psiatewriter");

while (<WHO>) (

unless (/fred/) { # не показывать имя Фред print LPR $_:

> } close WHO;

close LPR;

Считивая из дескриптора who по одной строке, зтот фрагмент кода выводит в дескриптор lpr все строки, которне не содержат строкового значення fred. В результате на принтер внводятся только те строки, которне не содержат имени fred.

Вовсе не обязательно указывать в команде open только по одной команде за один прием. В ней можно задать сразу весь конвейєр. Например, следую-щая строка запускает процесе ls(l), который передает свои результати по каналу в процесе tail(l), который, в свою очередь, передает свои результати в дескриптор файла who pr:

open(WHOPR, "Is I tail -r I");



Использование функции fork



Еще один способ создания нового процесса — клонирование текущего Perl-процесса с помощью UNIX-функции fork. Функция fork делает то же самое, что и системний вызов fork(2): создает клон текущего процесса. Зтот клон (он называется порожденным процессом, а оригинал — родительским) использует тот же выполняемый код, те же переменные и даже те же открьггые файлы. Различаются зти два процесса по возвращаемому значенню функции fork: для порожденного процесса оно равно нулю, а для родительского — ненулевое (или undef, если зтот системний вызов окажется неудачним). Ненулевое значение, получаемое родительским процессом,— зто не что иное как идентификатор порожденного процесса. Ви можете проверить возвращае-мое значение и действовать соответственно:

if (!defined($child_pid = fork()) {

die "cannot fork: $!";

} elsif ($pid) {

4s я —

родительский процесе } else (

# я — порожденннй процесе >

Чтоби максимально зффективно использовать зтот клон, нам нужно изучить еще несколько функции, которьге восьма похожи на своих UNIX-тезок: зто функции wait, exit и ехес.

Самая простая из них — функция ехес. Зто почти то же самое, что и функция system, за тем исключением, что вместо запуска нового процесса для виполнения shell-команди Perl заменяет текущий процесе на shell. После успешного внполнения ехес Perl-программа исчезает, поскольку вместо нее виполняется затребованная программа. Например,



ехес "date";

заменяет текущую Perl- программу командой date, направляя результат зтой команди на стандартний вивод Perl-программи. После завершення команди date делать больше нечего, потому что Perl-программа давно исчезла.

Все зто можно рассматривать и по-другому: функция system похожа на комбинацию функции fork c функцией ехес, например:

# МЕТОД І... использование system:

system("date");

t МЕТОД 2... использование fork/exec:

unless (fork) (

# fork видала нуль, позтому я — порожденный процесе и я выполняю:

exec ("date") ; t порожденный процесе становится командой date

}

Использовать fork и exec таким способом — не совсем правильно, потому что команда date и родительский процесе "пыхтят" одновременно, их результати могут переметаться и испортить все дело. Как дать родительскому процессу указание подождать, пока не завершится порожденный процесе? Именно зто и делает функция wait; она ждет завершення данного (да и любого, если быть точним) порожденного процесса. Функция waitpid более разбор-чива: она ждет завершення не любого, а определенного порожденного процесса:

if (!defined($kidpid = fork()) (

# fork возвратила undef, т.е. неудача die "cannot fork: $!";

” elsif ($pid =" 0) {

# fork возвратила О, позтому данная ветвь — порожденный процесе exec("date") ;

# если exec терпит неудачу, перейти к следующему оператору die "can't exec date: $!";

} else {

# fork возвратила не 0 и не undef,

# позтому данная ветвь — родительский процесе waitpid($kidpid, 0) ;

}

Если все зто кажется вам слишком сложным, изучите системные вызовы fork(2) и ехес(2), отыскав материалы о них в каком-нибудь руководстве по ОС UNIX, потому что Perl просто передает вызовы зтих функций прямо в системные вызовы UNIX.

Функция exit обеспечивает немедленньш выход из текущего Perl-про-цесса. Она используется для прерывания Perl-программы где-нибудь посе-редине или — вместе с функцией fork — для вьшолнения Perl-кода в процессе с последующим выходом. Вот пример удаления нескольких файлов из каталога///?у? в фоновом режиме с помощью порожденного Perl-процесса:



unless (defined ($pid = fork)) ( die "cannot fork: $!";

}

unless ($pid) (

unlink </tmp/badrock.*>; # удалить зти файлн exit; # порожденный процесе останавливается здесь

)

# родительский процесе продолжается здесь

waitpid($pid, 0); # после уничтожения порожденного процесса нужно все убрать

Без использования функций exit порожденный процесе продолжал бы вьшолнять Perl-код (со строки "# родительский процесе продолжается здесь") — а как раз зтого нам и не нужно.

Функция exit может иметь необязательный параметр, служащий числовим кодом выхода, который воспринимается родительским процессом. По умолчанию выход производится с нулевым кодом, показывающим, что все прошло нормально.



Сводка операций, проводимых над процессами



Операции, служащие для запуска процессов, перечислены в таблице 14.1. Таблица 14.1. Операции запуска процессов

Операция Стандартний ввод Стандартний

вывод
Стандартний вывод ошибок Нужно ли ожидать завершення процесса
System() Наследуется Наследуется Наследуется Да
от программы от программы от программы
Строка в обратных Наследуется от программы Принимается как строковое Наследуется от программы Да
кавычках значение
Запуск Соединен с Наследуется Наследуется Только во
процесса как деск дескриптором файла от программы от программы время вы-полнения
риптора файла для close ()
вывода при
помощи
команди
open()
Запуск Наследуется Соединен с Наследуется Только во
процесса как деск от программы дескриптором файла от программы время вы-полнения
риптора файла для close ()
ввода при
помощи
команди
open()
fork, Выбирается Выбирается Выбирается Выбирается
ехес, пользователем пользователем пользователем пользователем
wait,
waitpid
<


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

Простой способ получить асинхронний процесе (процесе, который по-зволяет продолжать выполнение Perl-программы до своего завершення) — открыть команду как дескриптор файла с созданием канала для стандартного ввода или стандартного вывода зтой команди. Команда, открытая как дескриптор файла для чтения, наследует стандартный ввод и стандартный вывод ошибок от Perl-программы; команда, открытая как дескриптор файла для записи, наследует от Perl-программы стандартный вывод и стандартный вывод ошибок.

Самый гибкий способ запустить процесе — заставить программу вызвать функции fork, ехес и wait или waitpid, которые полностью соответст-вуют своим UNIX-тезкам. С помощью зтих функции вы можете запустить какой-либо процесе синхронно или асинхронне, а также конфигурировать по своєму усмотрению стандартный ввод, стандартный вывод и стандартный вывод ошибок*.



Передача и прием сигналов



Один из вариантов организации межпроцессного взаимодействия осно-ван на передаче и приеме сигналов. Сигнал — зто одноразрядное сообщение (означающее, что "произошло данное событие"), которое посылается в процесе из другого процесса или из ядра. Сигналам присваиваются номера, обычно из диапазона от единицы до небольшого числа, например 15 или 31. Одни сигналы (фиксированные) имеют предопределенное значение и посы-лаются в процесе автоматически при возникновении определенных обстоя-тельств (например, при сбоях памяти или в исключительных ситуациях, возникающих при выполнении операций с плавающей запятой). Другие сигналы генерируются исключительно пользователем из других процессов, но не из всех, а только их тех, которые имеют разрешение на передачу сигналов. Передача сигнала разрешается только в том случае, если вы являєтесь привилегированным пользователем или если передающий сигнал процесе имеет тот же идентификатор пользователя, что и принимающий.



Ответ на сигнал называется действием сигнала. Фиксированные сигналы выполняют определенные действия по умолчанию, например, осуществляют прерывание или приостановку процесса. Остальные сигналы по умолчанию

* Полезно таюке знать о формах типа open(STDERR, ">&stdout") , используемых для точной настройки дескрипторов файлов. См. пункт open в главе 3 книги Programming Perl или на man-странице per1func(l).

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

Все зто аналогично тому, что делается и в других языках программиро-вания, но сейчас излагаемый материал приобретет специфический Perl-от-тенок. Когда Perl-процесе перехватывает сигнал, асинхронне и автоматиче-ски вызывается указанная вами подпрограмма, моментально прерывая выполнявшийся до нее код. Когда зта подпрограмма завершается, вьшолне-ние прерванного кода возобновляется, как будто ничего не случилось (за исключением появлення результатов действий, внполненных зтой подпро-граммой,— если она вообще что-нибудь делала).

Обычно подпрограмма-обработчик сигнала делает одно из двух: преры-вает программу, выполнив "очистку", или устанавливает какой-то флаг (например, глобальную переменную), которую данная программа затем проверяет*.

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

Имена сигналов определяются на man-странице signal(2), а также, как правило, в подключаемом С-файле /usr/include/sys/signal.h. Зти имена обычно начинаются с букв sig, например sigint, sigquit и sigkill. Чтобы обьявить подпрограмму my_sigint_catcher () обработчиком сигнала sigint, мы должны установить соответствующее значение в специальном хеше %sig. В зтом хеше в качестве значення ключа int (зто sigint без sig) следует указать имя подпрограммы, которая будет перехватывать сигнал sigint:



$SIG{'INT'} ” 'my_sigint_catcher';

Но нам понадобится также определение зтой подпрограммы. Вот пример простого определения:

sub my_sigint_catcher (

$saw_sigint = 1; # установить флаг }

Данный перехватчик сигналов устанавливает глобальную переменную и сразу же возвращает управление. Выполнение программы продолжается с той позиции, в которой оно было прервано. Обычно сначала обнуляется флаг $saw_sigint, соответствующая подпрограмма определяется как перехватчик сигнала sigint, а затем следует код основной программы, например:

$saw_sigint = 0; # очистить флаг $SIG{'INT') = 'my_sigint_catcher'; # зарегистрировать перехватчик

foreach (@huge_array) (

# что-нибудь сделать

t

еще что-нибудь сделать

# и еще что-нибудь if ($saw_sigint) ( # прерывание нужно?

t здесь "очистка" last;

} } $SIG('INT'} = 'DEFAULT'; # восстановить действие по умолчанию

* Попытка вьшолнения действий более сложных, чем вышеописанные, вероятнее всего, запутает ситуацию; большинство внутренних механизмов Perl "не любят", когда их вызы-вают одновременно в основной программе и из подпрограммы. Ваши системныс библио-теки тоже зтого "не любят".

Особенность использования сигнала в данном фрагменте программы состоит том, что значение флага проверяется в важнейших точках процесса вычисления и используется для преждевременного выхода из цикла; при зтом выполняется и необходимая "очистка". Обратите внимание на послед-ний оператор в приведенном выше коде: установка действия в значение default восстанавливает действие конкретного сигнала по умолчанию (следующий сигнал sigint немедленно прервет вьшолнение программы). Еще одно полезное специальное значение вроде зтого — ignore, т.е. "игнорировать сигнал" (если действие по умолчанию — не игнорировать сигнал, каку sigint). Для сигнала можно установить действие ignore, если не нужно виполнять никакой "очистки" и вы не хотите преждевременно завершать выполнение основной программы.

Один из способов генерирования сигнала sigint — заставить пользователя нажать на клавиатуре терминала соответствующие прерыванию клавиши (на-пример, [Ctrl+C]). Процесе тоже может генерировать сигнал sigint, используя для зтого функцию kill. Данная функция получает номер или имя сигнала и посылает соответствующий сигнал в процессы (обозначенные идентификато-рами) согласно списку, указанному после сигнала. Следовательно, для передачи сигнала из программы необходимо определить идентификаторы процессов-по-лучателей. (Идентификаторы процессов возвращаются некоторыми функция-ми, например функцией fork, и при открьггии программы как дескриптора файла функцией open). Предположим, вы хотите послать сигнал 2 (известный также как sigint) в процессы 234 и 237. Зто делается очень просто:



kill(2,234,237); # послать SIGINT в 234 и 237 kill ('INT', 234, 237); # то же самое

Более подробно вопросы обработки сигналов описаны в главе 6 книги Programming Perl и на man-странице perlipc(l).



Упражнения



Ответы к упражнениям см. в приложении А.

Напишите программу, которая получает результат команды date и вычис-ляет текущий день недели. Если день недели — рабочий день, виводить get to work, в противном случае виводить go play.

Напишите программу, которая получает все реальнне имена пользователей из файла /etc/passwd, а затем трансформирует результат команды who, заменяя регистрационное имя (первая колонка) реальным именем. (Совет:

создайте хеш, где ключ — регистрационное имя, а значение — реальное имя.) Попробуйте вьшолнить зту задачу с использованием команды who как в обратных кавычках, так и открьггой как канал. Что легче?

Модифицируйте предыдущую программу так, чтобы ее результат автомати-чески поступал на принтер. (Если у вас нетдоступа к принтеру, то, вероятно, вы можете послать самому себе сообщение злектронной почты.)

Предположим, функция mkdir перестала работать. Напишите подпро-грамму, которая не использует mkdir, а вызывает /bin/mkdir c помо-щью функции system. (Убедитесь в том, что она работает с каталогами, в именах которых єсть пробел.)

Расширьте программу из предыдущего упражнения так, чтобы в ней устанавливались права доступа (с помощью функции chmod).

|     Назад     |     Вперед     |



| Содержание | Предисловие | Введение | Ссылки
| Глава 1 | Глава 2 | Глава 3 | Глава 4 | Глава 5 | Глава 6 | Глава 7 | Глава 8 | Глава 9 | Глава 10
| Глава 11 | Глава 12 | Глава 13 | Глава 14 | Глава 15 | Глава 16 | Глава 17 | Глава 18 | Глава 19
| Приложение А | Приложение Б | Приложение В | Приложение Г |


Содержание раздела