Дмитро Ковальов
Б╕льш╕сть того, що можна написати про Перл, буде справедливим для будь-яко╖ (або хоча б для б╕льшост╕) систем, в яких Перл працю╓. Але те, як виконувати програми написан╕ на Перл╕, у б╕льшост╕ випадк╕в буде специф╕чним для т╕╓╖, або ╕ншо╖ системи.
Я маю досв╕д роботи з двома верс╕ями (напрямками) Перлу. а саме - Перл для Юн╕кса (власне, ори╜╕нальний Перл) ╕ Перл для Мак╕нтоша (MacPerl). MacPerl ╓ дещо специф╕чним ╕ взагал╕ заслугову╓ на окремий розгляд, а ╕нш╕ верс╕╖ Перла (так╕, як MS-DOS, Windows тощо) мене особисто не ц╕кавлять.
Тому, вважаючи на написане в попередньому абзац╕, надал╕ мова йтиме т╕льки про використання Перлу в т╕й або ╕нш╕й верс╕╖ Юн╕ксу, або його пох╕дних (таких, як наприклад Л╕накс).
Тож, перш, н╕ж намагатися виконувати програму в Перл╕, було б непагано для початку впевнитися, що в╕н встановлений в дан╕й систем╕. В цьому допоможе команда 'which'. Якщо з вашою системою (а б╕льше нав╕ть не з системою, а з середовищем користувача) все в порядку, то ви побачите на екран╕ щось под╕бне до наступного:
dk@sophy $ which perl
/usr/bin/perl
dk@sophy $
В трьох приведених рядках частина написана л╕воруч в╕д знаку долара: 'dk@sophy $' ╓ системним запрошенням, або як його ╕нколи називають системою п╕дказкою ╕ може бути (напевне буде) в╕дм╕нним, в╕д того, що ви побачите на сво╓му екран╕. Другий рядок ╓ результатом роботи команди 'which', ╕ ╓ маршрутом (дор╕жкою) до програми 'perl'.
Якщо Перл не встановлен╕й у ваш╕й систем╕, або якщо програма 'which' не може його знайти, то в залежност╕ в╕д тих або ╕нших фактор╕в ви можете побачити щось под╕бне до одного з наступних вар╕ант╕в:
(1)
dk@sophy $ which perl
dk@sophy $
(2)
dk@sophy $ which perl
which: no perl in (/usr/local/qt/bin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin:)
dk@sophy $
(3)
dk@sophy $ which perl
perl: Command not found.
dk@sophy $
В першому вар╕ант╕ програма 'which' не знайшла прграму 'perl' ╕ вир╕шила не розстроювати користувача цим фактом ╕ тому, просто зробила вигляд, що не чула запитання. У вар╕антах 2 ╕ 3 'which' подала зв╕ти про результати сво╖х пошук╕в дещо р╕зн╕ за сво╓ю зм╕стовн╕стю. Але у вс╕х трьох вар╕антах нас б╕льше всього ц╕кавить саме той сумний факт, що програма Перл не може бути знайденою в ц╕й систем╕ ╕ наше навчання в╕дклада╓ться на деякий час.
Прошу читача звернути увагу на деяк╕ нюанси: я написав "не може бути знайденою" на в╕дм╕ну в╕д набагато б╕льш категоричного "не встановлена". Може бути так, що програма встановлена в систем╕, але не дивлячись на це 'which' не може ╖╖ в╕дшукати. Це може бути насл╕дком дуже багатьох причин, основною серед яких ╓ р╕зн╕ нестандартн╕ маршрути до програми. З вар╕анту 2 видно, що 'which' шука╓ протр╕бну користувачев╕ програму в певних директор╕ях. Якщо Перл встановлено в директор╕╖, яка не входить до "поля зору" 'which', то зрозум╕ло, що Перл не буде знайдено.
Допомогти в╕дшукати Перл в систем╕ можуть ╕нш╕ системн╕ засоби, так╕ як, наприклад, 'find', 'locate', 'rpm' (дв╕ останн╕ програми характерн╕ для Л╕накса б╕льше, н╕ж для ╕нших Юн╕кс╕в, причому остання з них ++ т╕льки для Л╕накс╕в, що базуються на RedHat под╕бн╕й систем╕ - так╕й як, сам RedHat, Caldera, Mandrake, TurboLinux, тощо).
Подальш╕ пошуки перл╕в в файлових системах залишимо у вигляд╕ вправи для допитливого користувача. Якщо Перл в систем╕ не встановлено, то вих╕д з ц╕╓╖ ситуац╕╖ дуже простий - його треба встановити. ╤ я дуже радий заявити, що я зн╕маю з себе в╕дпов╕дальн╕сть за це, бо це виходить з поля зору даного твору ╕ за допомогою в цьому випадку треба звертатись до системних адм╕н╕стратор╕в.
Тож вважа╓мо, що невеличка вправа з програмкою 'which' зак╕нчилася усп╕шно ╕ перейдем дал╕. А саме до того, як отримати трошки б╕льше ╕нформац╕╖ про св╕й Перл.
Важливою ╕нформац╕╓ю про Перл ╓ його номер верс╕╖. В сучасному св╕т╕ ╕снують дв╕ верс╕╖ Перл╕в - верс╕я 4 та верс╕я 5. Кожна з них под╕ля╓ться на б╕льш др╕бн╕ п╕дверс╕╖ (тобто 4.036, 5.003, 5.005_05 тощо), але на ц╕ др╕бниц╕ ми не будемо звертати уваги ++ оск╕льки основн╕ в╕дм╕нност╕ лежать в перших розрядах верс╕╖.
Для б╕льшост╕ вправ нав╕ть в╕дм╕нн╕сть м╕ж верс╕╓ю 4 та 5 буде несутт╓вою, але все-таки краще знати, що так╕ в╕дм╕нност╕ ╕снують.
Щоб д╕знатися, яка верс╕я Перлу встановлена, надрукуйте таку команду в командному рядку: "perl -v". ╤ ось що при цьому трапля╓ться:
dk@sophy$ perl -v
This is perl, version 5.005_02 built for i586-linux
Copyright 1987-1998, Larry Wall
Perl may be copied only under the terms of either the Artistic License or the
GNU General Public License, which may be found in the Perl 5.0 source kit.
Complete documentation for Perl, including FAQ lists, should be found on
this system using `man perl' or `perldoc perl'. If you have access to the
Internet, point your browser at http://www.perl.com/, the Perl Home Page.
dk@sophy$
Зрозум╕ло, що в першому рядку цього пов╕домлення саме ╕ вказу╓ться верс╕я Перлу. Зрозум╕ло також, що отримавши в╕д користувача параметр "-v", Перл не намага╓ться виконувати н╕яко╖ програми, а просто друку╓ на екран╕ номер верс╕╖ ╕ зак╕нчу╓ роботу.
Для найпрост╕шо╖ програми в Перл╕ вам не потр╕бно буде редагувати н╕яких файл╕в, не потр╕бно комп╕лювати, в╕дладжувати, тощо. Ми створимо ╕ викона╓мо вашу першу програму прямо зараз: в той час, як ви це чита╓те.
По конях! До командного рядка!
META р╕зниця м╕ж шел ╕ редактором
Надрукуйте цю дуже просту штуку в командному рядку:
dk@sophy$ perl -e 'print "Hello! I have written my first program! \n"'
Hello! I have written my first program!
dk@sophy$
Зверн╕ть увагу на р╕зн╕ лапки (одинарн╕ та подв╕йн╕) в цьому приклад╕. Вони важлив╕, як ╕ ╖х порядок.
Написання програм в Перл╕ просто в командному рядку без створення файл╕в не ╓ якимось р╕дк╕сним трюком чи звихненням або ж педагог╕чним прийомом. ╢ багато випадк╕в, коли написати одну-дв╕ команди в Перл╕, щоб "виловити" якусь корисну ╕нформац╕ю з файла ╓ набагато прост╕шим, н╕ж в╕дкривати текстовий редактор ╕ записувати файл (нав╕ть якщо текстовий редактор ╓ vi або emacs), а пот╕м виконувати цей файл.
Давайте подивимось трохи уважн╕ше на щойно написану та виконану програму.
Параметр "-e" вказу╓ Перлу, що п╕сля цього йде сама власне програма, а не назва файлу, в як╕й програма записана. Дал╕, записана в одинарних лапках ╕де текст програми. В нашому випадку це:
print "Hello! I have written my first program! \n"
Одинарн╕ лапки потр╕бн╕ для того щоб ╕золювати внутр╕шн╕й текст в╕д програмно╖ оболонки (shell), в як╕й ви працю╓те. ╤накше програмна оболонка буде намагатися виконати команди власноручно т╕ команди, що призначен╕ для Перла. Не у вс╕х випадках використовуються одинарн╕ лапки. Використання лапок в╕дноситься скор╕ше до в╕домства shell'а, н╕ж до Перла. Тому, щоб зрозум╕ти краще, що в╕дбува╓ться з цими лапками, зверн╕ться до документац╕╖ по т╕й оболонц╕, в як╕й ви працю╓те (тобто sh, csh, tcsh, bash, ksh чи ще що-небудь). Як бачите Юн╕кс забезпечу╓ можлив╕сть вибору в цьому в╕дношенн╕.
Внутр╕шн╕ лапки (подв╕йн╕ в нашому випадку) в╕дносяться вже до самого Перла. В них запису╓ться текст, який повинна вивести на екран команда 'print'. (Зрозум╕ло, що команда 'print' щось повинна друкувати - ╕накше зв╕дки би взялася ця назва? )
В к╕нц╕ рядка ще записана якась дивина: "\n". Ця штука завжди використову╓ться, коли треба розбити рядок на к╕лька рядк╕в, або як кажуть серйозн╕ люди, тобто програм╕сти: "Вставити символ к╕нця рядка". К╕нець рядка не повинен стояти в к╕нц╕ того рядка, що ви друку╓те. Ви його вставля╓те там де вам потр╕бно. Якщо ви трохи погра╓теся з вашою улюбленою програмою на Перл╕, то може бути таке:
dk@sophy$ perl -e 'print "Hello! \n I\n have wri\ntt\nen my first program! \n"'
Hello!
I
have wri
tt
en my first program!
dk@sophy$
Для того, щоб створити найпрост╕ший файл з програмою в Перл╕ теж не дуже багато потр╕бно. Що нам треба - це всього-навсього текстовий редактор. (Ось зовс╕м забув, чи я казав, що для всих цих вправ потр╕бен також комп'ютер? Якщо н╕, то потурбуйтеся про це також, поки ще не п╕зно.)
Тож в вашому улюбленому редактор╕... Нема╓ такого? Тод╕ бер╕ть emacs,
╕ не помилитесь. Деяк╕ б╕льше люблять, звичайно vi , але я не буду
займатися тим, щоб в╕длякувати читач╕в з самого початку. Любов до vi
приходить не зразу. Люди, як╕ користуются ним для програмування,
належать до особливого класу людей. Вони н╕коли в житт╕ не клацнуть
дв╕ч╕ мишкою на п╕ктограм╕ Microsoft Word'а. Нав╕ть до користувач╕в
emacs'у вони ставляться з деякою поблажлив╕стю, як доросла людина
робить сердитий вигляд, при вид╕ капризуючо╖ дитини, але при цьому
тихенько посм╕ю╓ться соб╕ у вуса. Користування vi нав╕ть ц╕лком
вр╕вноважену людину може довести
до сл╕з. Тому наш виб╕р пада╓ на emacs .
Якщо ви як ╕ ми вир╕шили спинитися на emacs'╕, то наступний ваш командний рядок буде таким:
dk@sophy$ emacs myperl.pl
Я перейняв на себе в╕дпов╕дальн╕сть визначити назву файлу вашо╖ Перл-програми. Але зам╕сть "myperl.pl" ви можете вказати, що ваш╕й
душ╕ ближче та р╕дн╕ше. Вибираючи назву файлу, що зак╕нчу╓ться на "pl" (або як кажуть мастит╕ - ма╓ розширення "pl" (*) ) ви ма╓те шанс вв╕мкнути в emacs'╕ режим Перлу.
emacs ма╓ р╕зноман╕тн╕ режими для п╕дтримки (**) багатьох мов програмування, в тому числ╕ ╕ для п╕дтримки Перл. Але я не можу гарантувати, що цей режим вв╕мкнеться в вашому редактор╕ автоматично. emacs'и можуть в╕др╕зняти файли один в╕д одного або за розширеннями, або ж за першим рядком файлу. Пролог (теж мова програмування) теж користу╓ться розширенням "pl", тому деяк╕ верс╕╖ emacs'╕в можуть розп╕знати цей файл як пролог-програму ╕ вмикнути режим Пролог зам╕сть Перл (або ж emacs може бути не настро╓ним зовс╕м ╕ не вмикнути зовс╕м н╕якого режиму).
Вмикнути режим Перл у вашому emacs'╕ можна так: натисн╕ть та в╕дпуст╕ть Esc ╕ п╕сля цього натисн╕ть `x' (латинська л╕тера x). П╕сля цього в самому нижньому рядку emacs'а л╕воруч з'явиться напис `M-x'. Це запрошення emacs'а до вводу команди. Надрукуйте в цьому рядку `perl-mode' ╕ натисн╕ть Enter .
Дал╕, в залежност╕ знову ж таки в╕д конф╕гурац╕╖ вашо╖ системи, а точн╕ше вашого emacs'у, ви ма╓те два вар╕анти - або Перл-режим вмикнеться або ж н╕. Ознакою того, що в╕н вмикнувся, буде слово `(Perl)' , написане в рядку статусу emacs'у (другий рядок знизу - найчаст╕ше в╕н позаний на екран╕ в ╕нверсному кольор╕). Д╕знатися що режим Перлу знайдено можна по пов╕домленню No match надрукованому в командному рядку emacs'а.
Якщо ви належите до т╕╓╖ категор╕╖ людей, яким не везе з встановленими пакетами ╕ програмами, ╕ режим Перл все-таки не встановлений у ваш╕й систем╕, зверн╕ться за допомогою до системного адм╕н╕стратора. Але не зважаючи на невезуч╕сть ви все-одно можете продовжувати редагувати вашу програму.
Файл програми буде складатися з одного ╓диного рядка. Тож напиш╕ть вже знайомий вам рядок в emacs'╕:
print "Hello! I have written my first program! \n"
╕ запиш╕ть файл. Цей рядок не ма╓ вже подв╕йних лапок, як не ма╓ ╕ самого 'perl -e'. Виконання програми трохи в╕др╕зня╓ться в╕д попереднього вар╕анту. В цьому випадку щоб виконати програму поверн╕ться знову в програмну оболонку ╕ надрукуйте в командному рядку команду:
dk@sophy$ perl mytest.pl
Hello! I have written my first program!
dk@sophy$
Як бачите з командного рядка зник параметр '-e' ╕ по цьому Перл визнача╓, що те, що йде п╕сля слова perl ╓ назвою файлу, який треба виконати, використовуючи ╕нтерпретатор Перл. Результат роботи програми, як бачите, не зм╕нився.
Все, що писалося до цього моменту, в╕дносилося як до Юн╕кса, так ╕ до ╕нших систем, що мають командний рядок (MSDOS або MPW у Мак╕нтош╕ для прикладу). Але на цьому схож╕сть зак╕нчу╓ться ╕ ми входимо в джунгл╕ чист╕с╕нького Юн╕ксу. Те, про що йдеться дал╕, можливо в Юн╕кс╕ ╕ т╕льки в Юн╕кс╕. Написан╕ ц╕л╕ кер╕вництва щодо того, як зробити так, щоб в Windows NT програму, написану на Перл╕, можна було-би запускати так, як це робиться в Юн╕кс╕.
Ми ж, не гаючи часу на дурниц,╕ переходимо зразу до справи. Чому не можна виконувати програму так, як ми це вже робили в попередн╕й вправ╕? Можна, але не завжди зручно. Можливо ви захочете наприклад переписати свою стару програму (або як кажуть "скрипт") в мов╕ shell або awk, ╕ зробити ╖╖ програмою в Перл╕. Якби не було можливо вказати, яка програма використову╓ться для виконання того, чи ╕ншого скрипту, то кожного разу довелося б вказувати конкретно це в командному рядку. Тобто програма в Перл╕ завжди повинна запускатися "perl program", скрипт в мов╕ sh завжди б запускався як "sh program". А що, якщо я хочу написати скрипт, який би запускався просто як "program"? Що мен╕ робити?
Спробу╓мо запустити таким чином наш скрипт. Друку╓мо:
dk@sophy$ ./mytest.pl
./mytest.pl: Permission denied.
dk@sophy$
Допитливий Читач:
Гм-м-м, по-перше, нащо тут крапка з косою? А по-друге що таке
"Permission", ╕ чому воно "denied"?
Автор:
Д.Ч.:
А.:
Треба зробити файл вашо╖ програми зд╕бним до виконання (executable). Той, хто ма╓ досв╕д роботи з MSDOS та його пох╕дними Windows'ами, зна╓, що в потойб╕чному св╕т╕ розширення файлу (те, що йде в ╕мен╕ файлу п╕сля крапки) визнача╓, чи може виконуватися цей файл чи н╕. Виконуються т╕льки файли з розширеннями com, exe та bat. В св╕т╕ Мак╕нтош╕в належн╕сть файлу до програм визнача╓ться типом файлу (який запису╓ться в ресурсн╕й г╕лц╕ файлу. Той, хто не зна╓ ╕ не здогаду╓ться, що таке "ресурсна г╕лка" нехай сприйме це як ще один факт в╕д якого нема╓ н╕яко╖ корист╕ ╕ якими так переповнене наше життя). Щоб система виконувала файл, як програму (могла передати на нього управл╕ння - говорячи по-розумному), файл повинен мати тип APPL.
В Юн╕кс╕ ж все визнача╓ться дозволами (eng. permissions) на файл. Щоб система виконала файл, як програму, в╕н повинен мати дозв╕л на виконання. ╤ не просто дозв╕л, а дозв╕л для певного користувача. Не дуже вдаючися у подробиц╕ дозвол╕в та р╕зниц╕ в правах користувач╕в, зазначу зразу, що команда для встановлення потр╕бних для виконання дозвол╕в буде така:
chmod +x mytest.pl
Деяка розшифровка: остання частина команди означа╓ файл, якому встановлюються дозволи, chmod походить в╕д "change mode", а +x означа╓, що треба додати дозволи на виконання до цього файлу. П╕сля виконання ц╕╓╖ команди, виконувати наш скрипт буде дозволено будь-кому. Можливо також селективне встановлення дозвол╕в в залежност╕ в╕д приналежност╕ користувача до т╕╓╖ або ╕ншо╖ групи. За подальшою ╕нформац╕╓ю див╕ться "man chmod".
Нетерплячий Читач:
"Ну що, встановили дозволи, то-ж давай виконувати!.."
dk@sophy$ chmod +x mytest.pl
dk@sophy$ ./mytest.pl
./mytest.pl: print: command not found
dk@sophy$
Н.Ч.: "Га? Що дал╕?"
А.: Дал╕ -- Правило друге:
Мало того, щоб Юн╕кс знав, що файл можна виконата, ще йому треба вказати, як саме виконувати вашу програму. В Юн╕кс╕ ╓ для цього зас╕б, який найб╕льш в╕домий як "magic number", або чар╕вне число. (Майже те-ж саме, що ╕ чар╕вне слово - коли ви хочете запалити, то мало дозволити перехожому пригостити вас цигаркою, ще треба ╕ сказати "Будь-ласка"). Це чар╕вне число - два перших байти (дв╕ перших л╕тери) файлу. Прочитавши два цих перших байти Юн╕кс завжди може сказати до якого типу належить цей файл. Два перших байти будь-якого скрипту (будь-то скрипт у Перл╕, sh, csh чи ще чому завгодно -- наприклад нав╕ть такому екзотичному скрипт╕ як gnuplot -- систем╕ для граф╕чного зображення числових даних) повинн╕ бути "#!" (без лапок, звичайно).
Побачивши цих два маг╕чних байти, система почне придивлятися до файлу уважн╕ше, щоб визначити, який ╕нтерпретатор ╖й використовувати.
До реч╕:
Вс╕ командн╕ оболонки, вс╕ ц╕ sh, csh, ..sh ╓
╕нтерпретаторами, Перл також ╓ ╕нтерпретатором, правда трохи
╕ншого типу. Його можна назвати комп╕люючим ╕нтерпретатором
(чи може ╕нтерпретуючим комп╕лятором? А взагал╕, яка
р╕зниця?!). Тобто, зам╕сть того, щоб читати по одному рядку
╕ виконувати цей рядок, як роблять б╕льш╕сть командних
оболонок, Перл чита╓ зразу весь файл, комп╕лю╓ його весь, а
вже пот╕м викону╓. Це значно зб╕льшу╓ швидк╕сть роботи
скрипт╕в у Перл╕.
Тож в першому рядку файлу повинно бути записано, яким ╕нтерпретатором повинна користуватися система.
Але зв╕дки ж буде знати користувач, що треба записати в цьому першому рядку? Тут саме ╕ час знову згадати про команду which. Пам'ята╓те, як там було (в усп╕шному вар╕ант╕):
Я: "Which perl?"
Вона: "/usr/bin/perl"
Саме оцей рядок, разом ╕з чар╕вним словом треба написати в першому рядку програми. П╕сля цього наша програма вироста╓ аж вдв╕ч╕, зам╕сть одного рядка в н╕й ста╓ два:
#!/usr/bin/perl
print "Hello! I have written my first program! \n"
Що станеться, якщо програм╕ст забуде написати маг╕чне слово, або ж допустить помилку в написанн╕ повного рядка до Перл-╕нтерпретатора?
В Юн╕кс╕ ╓ домовлен╕сть, що якщо не вказана повна адреса ╕нтерпретатора, то Юн╕кс вважа╓, що цей скрипт написаний для найпрост╕шо╖ командно╖ оболонки - для /bin/sh В нашому випадку це означа╓, що /bin/sh намагатиметься виконати команду print , яко╖ нема╓ в мов╕ sh , ╕ звичайно вида╓ помилку про це.
Якщо ж в маршрут╕ до ╕нтерпретатора допущена помилка (або, наприклад, якщо ви на ╤нтернет╕ знаходите програма, яку так довго шукали. А ту програму написав хтось, у кого Перл був встановлений в зовс╕м ╕ншому м╕сц╕), то пов╕домлення про помилку буде зовс╕м ╕ншим. Щоб не бути голосл╕вним, давайте проведем досл╕д.
Для початку давайте впевнимося, що наша новенька програмка працю╓ так, як нам цього хочеться:
dk@sophy$ ./mytest.pl
Hello! I have written my first program!
dk@sophy$
Так... добре! Тепер зм╕н╕ть перший рядок, так щоб в ньому було написане щось зовс╕м ╕нше. Наприклад таке:
#!/USR/BIN/PERL
До реч╕:
Для тих, хто ще не звик до Юн╕кса, нагадаю, що вс╕ назви файл╕в в
Юн╕кс╕ в╕др╕зняють велик╕ ╕ мал╕ л╕тери. ╤ тому
#!/USR/BIN/PERL це зовс╕м не те, що #!/usr/bin/perl,
╕ нав╕ть не те, що #!/Usr/bin/perl.)
Викона╓мо ту-ж саму команду, що ╕ перед цим. ╤...
В залежност╕ в╕д того, в як╕й оболонц╕ ви в даний момент знаходитесь ви можете отримати р╕зн╕ в╕дпов╕д╕:
csh:
dk@sophy$ ./mytest.pl
./mytest.pl: Command not found.
dk@sophy$
bash:
dk@sophy $ ./mytest.pl
bash: ./mytest.pl: No such file or directory
dk@sophy $
sh:
sh$ ./mytest.pl
sh: ./mytest.pl: No such file or directory
sh$
Чому системн╕ команди так╕, наприклад, як ls, echo, startx та ╕нш╕ можна виконувати без всяких крапок та косих, що йдуть перед назвою файлу? ╤ чому, коли я працював в ДОС╕, я м╕г створити BATCH-файл "MYFILE.BAT" ╕ просто надрукувати в командному рядку "MYFILE" ╕ воно працювало? Чому вс╕ ц╕ складнощ╕ з Юн╕ксом? Чому, якщо файл знаходиться осьо тут, прямо в мене перед очима, в ц╕й директор╕╖, в як╕й я зараз ╓, чому я повинен перед ╕менем файлу писати ще як╕сь дурниц╕? Ось м╕й файл "mytest.pl", чому я не можу написати "mytest"? Чому я не можу написати "mytest.pl"? Чому я обов'язково маю писати "./mytest.pl"?
Як виявля╓ться вс╕ ц╕ дурниц╕, як зда╓ться на перший погляд, не ╓ зовс╕м дурницями, а мають св╕й великий пота╓мний зм╕ст. Обидв╕ системи (ДОС та Юн╕кс) мають поняття про таку р╕ч, як зм╕нна середовища PATH , яка ╓ просто перел╕ком директор╕й, в яких система веде пошук команд для виконання. Але використання ц╕╓╖ зм╕нно╖ сутт╓во в╕др╕зня╓ться в двох системах.
По-перше , про те в чому в╕др╕зняються ДОС ╕ Юн╕кс?
Коли в ДОС╕ користувач друку╓ команду в командному рядку, система почина╓ шукати цю команду. Посл╕довн╕сть пошуку така:
До цього додаються ще деяк╕ вар╕ац╕╖ щодо розширень файлу. Тобто, коли користувач друку╓ назву команди без розширення, то файли "*.COM" будуть пр╕оритет перед файлами "*.EXE", тощо.
Оск╕льки Юн╕кс не обмежу╓ться трьома (чи нав╕ть п'ятьма) розширеннями файл╕в, як╕ можуть бути програмами, то, звичайно-ж, треба завжди вказувати повну назву файлу, разом з розширенням.
В Юн╕кс╕ посл╕довн╕сть пошуку програми * ма╓ трохи ╕нший вигляд:
Як бачите в╕дсутня частина з пошуком в дан╕й директор╕╖. Чи можна зробити так, щоб система все-таки шукала файл в дан╕й директор╕╖? Можна. Для цього треба додати крапку (".") в маршрут пошуку - тобто в PATH . Але про це йдеться в...
По-друге : чому цього не варто робити?
Для безпеки. Перш за все. Пом╕чено давно, що парано╖ки сплять спок╕йн╕ше в╕д оптим╕ст╕в. Бо на вс╕х дверях у них висять величезн╕ запори, на в╕кнах - ╜рати, а сам╕ в╕конниц╕ позабиван╕ трьохдюймовими цвяхами.
Мати крапку в зм╕нн╕й PATH вважа╓ться (╕ справедливо вважа╓ться) дуже небезпечно. Розгляд цього питання аж занадто далеко в╕дходить в╕д програмування в Перл╕, тому я зупинюся просто на констатуванн╕ цього факту без будь-яких пояснень, а перейду до пояснення того, як зробити так, щоб ваша програма виконувалася ╕ щоб вам не треба було кожного разу друкувати назви директор╕й в командному рядку.
Можлив╕ два п╕дходи до цього.
Перш за все - а як д╕знатися, як╕ директор╕╖ записан╕ в зм╕нн╕й PATH . Виконайте для цього в командн╕й оболонц╕ команду:
dk@sophy$ echo $PATH
/usr/local/qt/bin:/sbin:/usr/sbin:/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
dk@sophy$
Назви директор╕й в╕дд╕ляються одна в╕д одно╖ двокрапками. Якщо ви ма╓те дозв╕л на запис в одну ╕з директор╕й виданих командою echo, то просто можете скоп╕ювати св╕й файл в цю директор╕ю. Якщо ж вс╕ директор╕╖ недоступн╕ для запису, то залиша╓ться вар╕ант з модиф╕кац╕╓ю PATH . Команда для цього зм╕ню╓ться в залежност╕ в╕д того, яким shell'ом ви користу╓тесь. Тому, зразу к╕лька вар╕ант╕в. Для конкретност╕ в╕зьмемо директор╕ю /tmp/bin ╕ додамо ╖╖ до зм╕нно╖ PATH .
csh та його пох╕дн╕ ( tcsh ):
setenv PATH $PATH:/tmp/bin
sh та його пох╕дн╕ ( ksh , bash , ash ):
PATH=$PATH:/tmp/bin
export PATH
В bash та ksh можливий також дещо коротший вар╕ант:
export PATH=$PATH:/tmp/bin
Ц╕ зм╕ни пропадуть в наступн╕й сес╕╖ (п╕сля того, як ви вийдете з системи ╕ вв╕йдете в не╖ знов). Тому, щоб зробити ц╕ установки пост╕йними ц╕ команди треба додати до одного з конф╕╜урац╕йних файл╕в вашого shell'у. Для csh (tcsh) це буде файл .login або .cshrc в домашн╕й директор╕╖. Для sh (ksh, zsh, bash, ash) це - файл .profile. bash також ма╓ к╕лька ╕нших файл╕в, як╕ визначають середовище користувача. Ц╕ файли - [[.bashrc, .bash_login, .bash_profile]]. Але в╕дм╕нност╕ м╕ж всима цими файлами краще пояснен╕ в man bash .
А на зак╕нчення цього розд╕лу все таки не можу втриматися в╕д ще одн╕╓╖ шпильки на адресу В╕ндовс╕в. Чому, знаючи про явний провал в безпец╕, ДОС все-таки дозволя╓ виконувати файли в поточн╕й директор╕╖? Та тому що, так чи ╕накше ви будете перевстановлювати систему (якби вона не називалась: DOS, Window NT, 3.1, 95, 98, 2000, 3000 ... 15000 - не дай Боже дожити до такого (!) ) в середньому дв╕ч╕ на м╕сяць. Тому яка р╕зниця в╕д чого вона загинула - чи в╕д "." в PATH, чи в╕д того, що MS Word'у не сподобалось те, що ви надрукували, а чи ще в╕д чого?
На цьому розд╕л про виконання програм написаних у Перл╕ можна вважати зак╕нченим. Дуже мало в цьому розд╕л╕ йшло мови власне про програмування в Перл╕. Розд╕л стосувався (як можливо хтось дуже уважний пом╕тив), про загальн╕ вимоги до будь-якого скрипту в Юн╕кс╕, не т╕льки скрипту написаного в Перл╕. Але розум╕ння основ виконання програм повинно передувати написанню програм. Ви ж не збира╓тесь писати програми, як╕ не виконуються?
А хто вже ц╕ основи знав, той все одно н╕чого не втратив, бо я не можу пов╕рити в те, що в╕н марнував св╕й власний час на ще одне перечитування.
Цей розд╕л я зак╕нчу п╕дсумуком т╕льки-що написаного.
В цьому розд╕л╕ я спробую п╕д╕йти трохи ближче до програмування в Перл╕. Ми вже б╕льше не будемо розглядати питання про те, як написан╕ нами програми повинн╕ виконуватися. Не будемо звертати уваги також ╕ на те, чи ма╓ програма в першому сво╓му рядку " #!/usr... ". Якщо результатом роботи скрипту все ще ╓ "Command not found", то мо╓ю ╓диною порадою будео ще раз уважн╕ше перечитати попередн╕й розд╕л.
Мабуть зрозум╕ло, що в середньому програми на Перл╕ складаються з б╕льше, н╕ж одн╕╓╖ команди. Тому, для того, щоб ╕нтерпретатор розум╕в, де зак╕нчу╓ться одна команда ╕ почина╓ться ╕нша, повинн╕ ╕снувати деяк╕ правила, як╕ визначають меж╕ команд.
В Перл╕ використову╓ться поняття "блоку", яке ╓ трохи ширшим, н╕ж команда. Блоком може бути, як одна одинока команда (яка назива╓ться "оператором"), так ╕ деяка лог╕чно по╓днана посл╕довн╕сть оператор╕в. Кожен блок повинен зак╕нчуватись крапкою з комою ";". ╤ блок, який склада╓ться з б╕льше, н╕ж одного оператора, береться у ф╕гурн╕ дужки:
{
...
... деякий_перл_оператор;
... ╕нший_перл_оператор;
... ще_один_перл_оператор;
...
};
Оск╕льки оператор ╓ не що ╕нше, як блок, який склада╓ться з одного оператора, то в подальшому я не буду робити велико╖ р╕зниц╕ м╕ж обома поняттями "оператор" та "блок", ╕ обидва терм╕ни будуть вживатися р╕вноправно, кр╕м особливих випадк╕в, коли така р╕зниця сутт╓ва. Але в таких випадках це завжди буде оговорюватися особливо.
Фраза про те, що _кожен_ блок повинен зак╕нчуватися крапкою з комою ╓, взагал╕-то, нев╕рною. Якби це було так, то наша програма з першого розд╕лу не працювала б. В╕рн╕ше буде сказати, що "блоки повинн╕ в╕дд╕лятися один в╕д одного крапкою з комою". З цього виплива╓, що останн╕й оператор в програм╕ не обов'язково ма╓ мати крапку з комою в к╕нц╕. Те ж стосу╓ться ╕ останнього оператора в блоц╕. Але поставити ╖╖ не буде великою помилкою.
Практично вс╕ мови програмування мають в соб╕ ту чи ╕ншу форму коментар╕в. Коментарем вважа╓ться шматок програми, на який комп╕лятор чи ╕нтерпретатор не звертають н╕яко╖ уваги. Застосовуються коментар╕ т╕льки для зручност╕ програм╕ст╕в, як╕ мають пот╕м читати цей код. Гарно в╕дкоментовану програму завжди легше зрозум╕ти.
В Перл╕ для коментар╕в використову╓ться знак "#". Все що йде п╕сля цього символу до самого к╕нця рядка вважа╓ться коментарем ╕ ╕гнору╓ться Перлом.
Зважаючи на все сказане ми можемо переписати нашу першу програму у такому вигляд╕:
#!/usr/bin/perl
# This script will print short message on the screen
# and exit after this.
# Оск╕льки коментар╕ ╕гноруються, то ╖х можна писати будь-якою мовою
print "Hello!";
# Результат роботи цього скрипту такий же, як ╕ попереднього
print " I have written my first program! \n"
╤ виконавши його ми отрима╓мо:
dk@sophy$ ./mytest.pl
Hello! I have written my first program!
dk@sophy$
Вс╕ мови програмування оперують зм╕нними. Деяк╕ з мов мають також поняття констант. Константа - це така зм╕нна, яка не ╓ зм╕нна (значення яко╖ встановлю╓ться один раз ╕ не зм╕ню╓ться в процес╕ роботи програми). Тобто, це практично одне ╕ те ж. Тому Перл не ма╓ констант. А зм╕нну можна уявити як деяку ком╕рку в яку можуть вкладатися т╕ або ╕нш╕ дан╕.
Б╕льш╕сть мов програмування мають поняття про типи даних. Перш, н╕ж користуватися якоюсь зм╕нною, треба визначитися, до якого типу належить ця зм╕нна. Тобто, якщо ви будете оперувати ц╕лими числами, то в╕дпов╕дна зм╕нна повинна бути ц╕лого типу. Якщо вам потр╕бно оперувати текстовими даними, то зм╕нн╕ повин╕ мати тип символу або рядка. В загальному випадку, не дозволяться в одн╕й операц╕╖ використовувати дан╕ р╕зних тип╕в. (Тобто, це б╕льше схоже на запитання: "Що буде, якщо до п'яти центнер╕в додати 2 к╕лометри?"
В р╕зних мовах ╕снують р╕зн╕ п╕дходи до визначення та оперування р╕зними типами даних. В деяких мовах вс╕ дан╕ потр╕бно декларувати перш, н╕ж використовувати. В ╕нших декларувати дан╕ не потр╕бно, але все визнача╓ться преф╕ксами до назв зм╕нних, тощо. В багатьох мовах програмування перш, н╕ж оперувати даними р╕зних тип╕в, ╖х треба перетворити в ╕нший тип. Наприклад, звичайно-ж з точки зору здорово╖ лог╕ки, можна додати ц╕ле число 5 ╕ десятковий др╕б 0,78. Але для тако╖ операц╕╖ (наприклад в Паскал╕) потр╕бно спочатку 5 перетворити ╕з ц╕лого числа в десятковий др╕б 5,0. Значення числа при цьому не зм╕ню╓ться, але зм╕ню╓ться тип зм╕нно╖. ╤ п╕сля тако╖ операц╕╖ вже обидва доданки стають десяктовими дробами, над якими дозволя╓ться виконувати арифметичн╕ д╕╖.
Р╕зн╕ мови програмування в╕др╕зняються одна в╕д одно╖ також щодо операц╕й перетворення даних. Деяк╕ з них вимагають завжди обов'язкового явного перетворення типу даних. Тобто не можна просто написати "5 + 0,78", а обов'язково треба написати щось под╕бне до "(дробове число, числове значення якого дор╕вню╓ 5) + 0,78". ╤нш╕ мови розум╕ють, в деяких випадках, що до чого ви хочете додати, ╕ роблять деяк╕ перетворення, покладаючись на правила перетворень даних.
Перл в цьому в╕дношенн╕ особливий. Тип╕в даних в розум╕нн╕ ╕нших ("дорослих") мов прогрмування в Перл╕ нема╓. ╢ один-╓диний скалярний тип даних в Перл╕, який покрива╓ вс╕ необх╕дн╕ типи: ц╕л╕ числа, дробов╕ числа, символи та рядки. ╤ вс╕ необх╕дн╕ перетворення робляться Перлом неявно, непом╕тно для програм╕ста ╕ робляться вони в залежност╕ в╕д контексту, в якому це перетворення трапля╓ться.
Кр╕м скалярного типу даних Перл ма╓ також масиви скаляр╕в та асоц╕ативн╕ масиви, про як╕ буде трохи дал╕. Вс╕ ц╕ три типи даних в╕др╕зняються один в╕д одного преф╕ксами.
Скалярний тип даних в Перл╕ познача╓ться преф╕ксом "$". Тому, побачивши написане $a= 5 або $b= 'one' , ви не помилитесь, якщо скажете, що обидва ц╕ вирази присвоюють значення не масивам, а скалярним зм╕нним. Правда, якщо в другому випадку, можна з великою долею ймов╕рност╕ сказати, що $a ╓ текстовим рядком, то в першому випадку н╕чого певного сказати не можна.
Подив╕ться уважн╕ше на наступн╕ приклади (для деяко╖ лакон╕чност╕ я буду надал╕ опускати системн╕ запрошення в командному рядку):
perl -e '$a=5; $b="one"; print $a + $b, "\n"'
5
perl -e '$a=5; $b="one"; print $a . $b, "\n"'
5one
В першому випадку зм╕нна $a тракту╓ться, як числова зм╕нна (бо викону╓ться операц╕я додавання) ╕ зм╕нна $b, не ╓ числовою, тому сума дор╕вню╓ першому доданку. В другому приклад╕ викону╓ться операц╕я над двома текстовими рядками * . Тому обидв╕ зм╕нн╕ трактуються в цьому випадку, як текстов╕ зм╕нн╕ ╕ ви бачите результат цього.
Можлив╕ нав╕ть зовс╕м дик╕ ситуац╕╖, коли в одн╕й частин╕ скрипту одна ╕ та ж зм╕нна тракту╓ться по одному, ╕ в ╕нш╕й частин╕ - по ╕ншому . В наступному приклад╕ зм╕нна $a в одному випадку виступа╓ як текстова зм╕нна, а в ╕ншому як числова.
perl -e '$a=5; $b="one"; $c=3; print $a . $b, "\n", $a + $c ,"\n"';
5one
8
Або ще один, нав╕ть б╕льш дивний, вар╕ант, в якому до результату конкатенац╕╖ двох текстових зм╕нних дода╓ться число. Тобто, спочатку до п'яти к╕лометр╕в дода╓ться ще ш╕сть к╕лометр╕в, ╕ пот╕м результат склада╓ться з двадцятьма коровами:
perl -e '$a=5; $b=15; $c=3; $d =$a . $b; print $d, "\n", $d + $c ,"\n"';
515
518
В самому першому приклад╕ я звертав увагу на те, що порядок використання лапок - важливий. Давайте трохи зупинимось на цьому питанн╕.
Перш за все - нав╕що взагал╕ використовувати лапки? Чи не прост╕ше просто написати що-небудь типу:
print Hello! I have written my first program! \n
Написати можна, але сам╕ можете впевнитися, що працювати це не буде. Так само, як пишучи твора, ми вид╕ля╓мо в твор╕ пряму мову, так само потр╕бно вид╕ляти текстов╕ еп╕зоди в програм╕. Текст, взятий в лапки ╓ прямою мовою в Перл-програм╕.
Ну, з цим зрозум╕ло, це була проста задача. Але як бути з оцими р╕зними типами лапок: з одинарними (''), та з подв╕йними ("") ?
Проведемо деяк╕ досл╕дження:
$a=" шматок тексту ";
$b=" ще один шматок ";
print $a . $b, "\n";
print "$a $b ","\n";
print '$a $b ',"\n";
./mytest.pl
шматок тексту ще один шматок
шматок тексту ще один шматок
$a $b
Що в╕дбува╓ться? Перший вар╕ант зрозум╕лий: просто конкатенуються дв╕ текстов╕ зм╕нн╕ ╕ результат цього об'╓днання друку╓ться. Другий рядок схожий на перший, але в ньому ╓ зайвий пропуск, але трет╕й вар╕ант сутт╓во в╕дм╕нний в╕д обох попередн╕х.
Почнемо з третього вар╕анту, бо з ним роз╕братися прост╕ше. Все взяте в одинарн╕ лапки друку╓ться так, як воно написане, тобто, не зважаючи на те, що всередин╕ лапок записане щось таке, що за виглядом нагаду╓ скалярн╕ зм╕нн╕ - $a $b . Але написане всередин╕ лапок тракту╓ться не як зм╕нн╕, а як просто текст, який друку╓ться буквально. ╤нтерпретатор Перла "не бачить" того, що знаходиться всередин╕ одинарних лапок.
В другому вар╕ант╕ (подв╕йн╕ лапки) все, що лежить всередин╕ лапок, ╓ видимим для Перла - подв╕йн╕ лапки "прозор╕". ╤ Перл п╕дставля╓ зам╕сть зм╕нних ╖х значення, або як ще кажуть Перл розширя╓ значення зм╕нних.
Це стосу╓ться не т╕льки оператора print , але ╕ будь яко╖ ╕ншо╖ операц╕╖ в Перл╕. Значення в подв╕йних лапках завжди розширяються:
$a=" шматок тексту ";
$b=" ще один шматок ";
$c = "$a плюс $b";
$d = '$a плюс $b';
print $c, "\n";
print $d, "\n";
./mytest.pl
шматок тексту плюс ще один шматок
$a плюс $b
Все, про що йшлося в останн╕х абзацах, носить назву "цитування" - quoting. ╤ справд╕, все це дуже схоже на те, як в текст╕ вставляються цитати. Чи ╓ ще ╕нш╕ правила цитування, кр╕м двох вже описаних правил з подв╕йними та одинарними лапками? ╢, ще одне. Коли потр╕бно захистити в╕д розширення один ╓диний символ, перед ним ставиться зворотня коса (або backslash) - "\". Наступний приклад демонстру╓ це правило:
$a=" шматок тексту ";
$b=" ще один шматок ";
print "$a плюс $b";
print "$a плюс \$b";
./mytest.pl
шматок тексту плюс ще один шматок
шматок тексту плюс $b
Але ситуац╕я з розширенням зворотьо╖ косо╖ насправд╕ дещо складн╕ша, н╕ж з розширенням зм╕нних. Всередин╕ одинарних лапок вона розширю╓ться. В наступному приклад╕ зворотня коса "маску╓" одинарну лапку, ╕ через це Перл вида╓ помилку.
perl -e "print '\'"
Can't find string terminator "'" anywhere before EOF at -e line 1.
Кр╕м описаного вже типу скалярних даних (як╕, як ми бачили, можуть в Перл╕ бути чим завгодно) Перл ма╓ ще масиви скалярних даних та асоц╕ативн╕ масиви скаляр╕в.
META
* масиви
* асоц╕ативн╕ масиви - hash
Питання роботи з файлами насправд╕ ╓ трохи ширшим, н╕ж просто перел╕к операц╕й для того, щоб прочитати файл на диску або щоб створити новий файл чи дописати щось до вже ╕снуючого. В Перл╕ (особливо в тих системах, як╕ п╕дтримують командний рядок -- тобто не Macintosh -- та стандартн╕ дескриптори файл╕в, так╕ як STDIN , STDOUT , STDERR -- тобто не Windows) питання роботи з файлами включа╓ в соб╕ так╕ операц╕╖, як виконання системних команд, перехоплення виводу в╕д них та його використання, в╕дкриття системних канал╕в (pipe) ╕з Перл-програми, тощо. Якщо Ви не дуже розбира╓тесь у терм╕ноло╜╕╖, яка тут так часто вжива╓ться, ми спробу╓мо з цим трохи дал╕ роз╕братися, а про все, що не зрозум╕ло з пояснення в ц╕й книжц╕, доведеться почитати в книжц╕ по Юн╕ксу.
Але що то таке - STDIN ? Це ╓ скорочення в╕д англ╕йського "standard input" ╕ означа╓ "стандартний вв╕д". Тепер вже зрозум╕ло, що "standard output" або STDOUT - це стандартний вив╕д, а "standard error" - це STDERR , який за неможлив╕стю в╕дшукати б╕льш пристойного укра╖нського екв╕валенту, я перекладаю як "стандартний пристр╕й для пов╕домлення помилок". Розум╕ння правил роботи з цими пристроями ╓ сутт╓вим для розум╕ння роботи б╕льшост╕ програм у Юн╕кс╕, оск╕льки абсолютна б╕льш╕сть програм, написаних в "юн╕кс╕вському стил╕", вм╕ють читати дан╕ з STDIN , передавати результати сво╓╖ роботи на STDOOUT та пов╕домляти про помилки в робот╕ на STDERR .
Оск╕льки поняття "файл" використову╓ться в Юн╕кс╕ дуже часто, ми говоритимо дал╕ про стандартний вв╕д, вив╕д та пристр╕й для пов╕домлення помилок, як про звичайн╕ файли. ╤ в сво╖й сут╕ вс╕ ц╕ тро╓ дуже мало в╕др╕зняються в╕д звичайних файл╕в (тобто файл╕в на диску). В╕дм╕нн╕стю з точки зору програм╕ста ╓ т╕льки те, що вс╕ ц╕ тро╓ ╓ вулицями з односторонн╕м рухом. ╤з STDIN можна т╕льки читати, в STDOUT та STDERR можна т╕льки писати.
Перш, н╕ж братися до вивчення програмування з застосуванням нових для нас понять, давайте спробу╓мо роз╕братися з ними на простих прикладах. Розглянемо дуже просту команду cat (1) . Можна було-б описати цю команду, як таку, яка роздрукову╓ один або к╕лька файл╕в на екран╕ дисплея. ╤ це буде в╕рно до деяких п╕р. Так, наприклад, команда
cat myfile.txt myfile2.txt
д╕йсно не зробить н╕чого надприроднього. Вона просто надруку╓ файли з назвами myfile.txt та myfile2.txt на екран╕ один за одним. Але якщо зм╕нити цю команду на таку:
cat myfile.txt myfile2.txt > myfiles.txt
то все зм╕ниться дуже швидко. Зам╕сть того, щоб друкувати файли на екран╕, ця команда з╕лл╓ два файли ╕ запише ╖х обидва в трет╕й файл ╕з назвою myfiles.txt . Якщо такого файлу не ╕сну╓, команда його створить, а якщо в╕н ╕сну╓, то зам╕нить його новим.
якщо Вашою робочою оболонкою ╓ csh або tcsh
cat myfile.txt myfile2.txt > myfiles.txt
myfiles.txt: File exists
або якщо Ви працю╓те в bash '╕
cat myfile.txt myfile2.txt > myfiles.txt
bash: myfiles.txt: cannot overwrite existing file
Це означа╓, що Ви пробу╓те переписати командою cat вже ╕снуючий файл. При цьому зм╕нна `noclobber' встановлена в Ваш╕й командн╕й оболонц╕. ╤снують два можливих шляхи вир╕шення ц╕╓╖ проблеми:
Перший - в╕дшукати де саме в Ваших стартових скриптах ( .profile , .login , .bashrc чи .cshrc ) встановлю╓ться ця зм╕нна ╕ в╕дм╕нити цю установку (Установка ма╓ такий вигляд: set noclobber або set -o noclobber ),
або Другий - використати такий синтаксис команди:
cat myfile.txt myfile2.txt >| myfiles.txt
Саме тут ╕ виявля╓ться, що визначення команди cat , як тако╖, що роздрукову╓ файли на екран╕, яке було дане всього к╕лька абзац╕в тому вже по сут╕ не в╕рне. Але в робот╕ команди н╕чого не зм╕нилося. Отже проблема скор╕ше з нашим визначенням. Тому в╕рним визначенням команди буде таке: "Команда cat конкатену╓ файли, як╕ вказуються в командному рядку ╕ роздрукову╓ ╖х на стандартний вив╕д." Просто в першому випадку стандартним виводом був дисплей системи, а в другому - файл на диску. Стандартним виводом для команд ╓ дисплей комп'ютера, якщо не вказано ╕накше. В тому випадку, коли особливо сказано, що вих╕дн╕ дан╕ мають бути записаними у файл (тобто ">file" , командна оболонка перебира╓ на себе функц╕╖ по п╕дтриманню такого файлу та пересиланню у нього даних в╕д команди.
В╕дм╕тьте: Записом даних у файл займа╓ться не сама команда, а командна оболонка ( shell ). Тому, анало╜╕чно працюють вс╕ команди Юн╕кса. Тож варто весь час пам'ятати, що ц╕ команди друкують не на екран╕, а на STDOUT .
╤нш╕ дв╕ команди допоможуть нам трохи роз╕братися з STDIN та STDOUT разом. Розглянемо для початку таку команду, як bc(1) . Документац╕я до bc визнача╓ його, як мову калькулятора з дов╕льною точн╕стю. Якщо Ви н╕коли до цього не користувалися ц╕╓ю командою, проведемо коротенький екскурс в основи користуванням нею. Коли Ви просто в командному рядку надруку╓те bc , Ви потрапля╓те в ╕нтерактивний режим роботи калькулятора. Тут Ви можете виконувати арифметичн╕ д╕╖ дов╕льно╖ точност╕ ╕ складност╕ над (Увага!) ц╕лими числами. Ви просто набира╓те д╕╖ на клав╕атур╕, тиснете Return ╕ ма╓те результат. Ось, наприклад, що я маю на сво╓му екран╕:
$ bc
bc 1.05
Copyright 1991, 1992, 1993, 1994, 1997, 1998 Free Software Foundation, Inc.
This is free software with ABSOLUTELY NO WARRANTY.
For details type `warranty'.
2+2
4
5/2
2
quit
$
Я просто додав 2 ╕ 2 ( 2+2 Return ), а пот╕м розд╕лив 5 на 2 ( 5/2 Return ).
В такому простому (звичайному калькуляторному) режим╕ використання команда сприйма╓ дан╕ ╕з STDIN (яким в цьому випадку ╓ клав╕атура комп'ютера) ╕ переда╓ результати роботи на STDOUT (як ми вже зна╓мо з попередньо╖ дискус╕╖, ним ╓ екран комп'ютера). Але Юн╕кс би не був Юн╕ксом, якби все було так просто - ╓ соб╕ програма калькулятора, раху╓ш в ньому що тоб╕ потр╕бно, запису╓ш результат на пап╕рц╕ ╕ п╕сля цього з пап╕рця перепису╓ш у текстовому редактор╕ циф╕рки туди, де вони потр╕бн╕. Н╕! В Юн╕кс╕ люди поводять себе ╕накше. Коли мен╕, скаж╕мо, треба перевести IP-адресу, виражену в десятков╕й форм╕ у ш╕стнадцяткове ╖╖ подання, я займусь тим, що напишу скрипт, який буде виконувати все одн╕╓ю командою. На написання скрипту у мене п╕де вп'ятеро б╕льше часу, н╕ж на обчислення цих даних за допомогою калькулятора ╕ пап╕рця, але ж у мене буде готовий скрипт, який я зможу використати наступного разу, коли мен╕ потр╕бно буде зробити таке саме перетворення. Одна т╕льки б╕да - навряд чи мен╕ коли-небудь знадобиться переводити десятковий IP в ш╕стнадцятковий...
Тож, як написати скрипт (поки-що не Перл, а звичайний шел), який би переводив числа ╕з одн╕╓╖ системи числення в ╕ншу? Для цього, як Ви вже мабуть зрозум╕ли, треба зайнятися перенаправленнями STDIN та STDOUT туди-сюди. Тобто, треба вс╕ т╕ команди, як╕ ми друку╓мо на клав╕атур╕, подати на STDIN команди bc . Зробити це можна якоюсь ╕ншою командою, або просто записати у файл. Ми виберемо перший - командний - шлях. Невеличкий експеримент:
$ echo "2+2" | bc
4
Працю╓! Тепер треба було-б ╕ к╕лька команд bc п╕дучити... Зробимо таке:
$ echo "obase=16; 127" | bc
7F
Команда obase в bc встановлю╓ вих╕дну систему числення, тобто те в якому вигляд╕ калькулятор вида╓ результати обчислень. Неважко здогадатися, що ibase в такому випадку визнача╓ вх╕дну систему числення. ╤ тому наступний приклад переводить число ╕з дв╕йково╖ системи у в╕с╕мкову:
$ echo "obase=8; ibase=2; 11110100010101000101" | bc
3642505
Тож ми вже пов╕стю готов╕ до того, щоб написати наш знаменитий скрипт. Втн буде мати такий вигляд:
#!/bin/sh
echo "obase=16; $1" | bc
Оце ╕ все? Так, все... А працю╓ в╕н так:
$ ./h2d 20
14
$ ./h2d 100
64
$ ./h2d 15
F
$ ./h2d 225
E1
Як все це сказане про екран та STDOUT сп╕вв╕дноситься ╕з програмуванням на Перл╕? Дуже просто. Оператор print Перла друку╓ все, що його просять, не на екран╕, а на STDOUT , кр╕м тих випадк╕в коли вказано ╕накше. Саме т╕ випадки, коли вказано ╕накше, ми ╕ будемо розглядати дал╕.
Повна форма оператора print ( man perlfunc ) ма╓ наступний вигляд:
print FILEHANDLE LIST
В цьому визначенн╕ LIST ╓ тим списком даних, що ма╓ надрукувати оператор. До цього моменту ми не вживали FILEHANDLE ╕ не зна╓мо, що це таке. Ми обмежимся тим, що будемо розглядати FILEHANDLE як саму звичайну зм╕нну спец╕ального типу, яка пов'язу╓ файл, в який потр╕бно виводити дан╕ з його назвою. Зауважте, FILEHANDLE - це не назва файлу (так саме, як, наприклад, STDOUT - теж не ╓ назвою файла)! Це - всього лиш деякий символ, який зв'язу╓ файл ╕з зовн╕шн╕м св╕том, така соб╕ "ручка в╕д файлу" (саме так можна перекласти з англ╕йсько╖ "filehandle").
Нав╕що потр╕бен FILEHANDLE , якщо можна було б просто надрукувати щось под╕бне до:
print "/home/user/MyFile.txt" "Hello"
Причина цього таж сама, що ╕ ╕снування зм╕нних в мовах програмування. Набагато зручн╕ше оперувати зм╕нними в програмах, н╕ж ╖х значеннями.
Перш, н╕ж у файл можна буде надрукувати будь-що, його треба в╕дкрити. П╕сля зак╕нчення виводу у файл, його треба закрити. Хоча ця остання операц╕я ╕ необов'язкова - п╕сля виконання скрипту, вс╕ в╕дкрит╕ файли автоматично закриваються. З точки зору програм╕ста операц╕я в╕дкриття файлу - це просто операц╕я, яка зв'язу╓ назву файлу ╕з в╕дпов╕дним FILEHANDLE'ом.
Добре, якщо кожен файл потр╕бно в╕дкривати перш, н╕ж писати у нього, то чому ж тод╕ ми н╕чно не говорили ран╕ше про те, що треба в╕дкрити STDOUT коли ми прекрасно обходилися без цього в наших попередн╕х скриптах? Виявля╓ться, що ╓ ще деяк╕ реч╕, якими STDIN та STDOUT в╕др╕зняються в╕д звичайних файл╕в. В╕дм╕на заключа╓ться в тому, що STDOUT не треба в╕дкривати перед записом у нього - в╕н автоматично в╕дкритий при старт╕ будь-якого Перл╕вського скрипта. Теж саме стосу╓ться ╕ STDIN - в╕н також автоматично в╕дкрива╓ться при старт╕ ╕нтерпретатора Перла. Тому то ми можемо без будь-яких зайвих сл╕в просто написати print "Hello" ╕ ця операц╕я викона╓ться.
Тож роз╕бравшись трохи з тим, що треба в╕дкривати, а що не треба, ми можем на деякий чам забути про те, як саме в╕дкривати файли. Зам╕сть цього ми можемо зайнятися вивченням використання тих файл╕в, як╕ в╕дкривати не потр╕бно - тобто STDIN та STDOUT .
Про STDOUT вже писалося вище. Поки що ми не можемо додати чогось сутт╓во нового до того, що вже в╕домо. Тому давайте займемося досл╕дженням STDIN . Як╕ ╕снують способи використання цього файлу, який так послужливо в╕дкритий для нас самим Перлом?
Мабуть це буде - найпрост╕ший вар╕ант використання STDIN у Перл скрипт╕. Хоча, в╕дм╕тимо зразу ж, ╕ не самий розповсюджений, а також у б╕льшост╕ випадк╕в ╕ не рекомендований для використання через деяк╕ не зовс╕м улагоджен╕ в╕дносини цього оператора з правилами безпеки.
Коли ми в перший раз почина╓мо ц╕кавитися тим, що таке STDIN , STDOUT та STDERR при вивченн╕ Юн╕кса? Саме тод╕, коли нам потр╕бно виконати, щось на зразок того, що ми нещодавно проробили ╕з командою cat - перехопити STDOUT в╕д команди ╕ записати його в файл. Або коли нам потр╕бно перехопити STDOUT ╕ передати його на STDIN ╕ншо╖ команди (мова йде про pipe або про канали в Юн╕кс╕), як, наприклад у такому випадку:
ls | wc -l
Що нам робити, коли ми хочемо використати деяку команду Юн╕кса ╕зсередини програми, написано╖ в Перл╕? Тут на допомогу приходить оператор `` (зворотн╕ апострофи). Синтаксис оператора дуже простий:
`<деяк╕ команди Юн╕кса>`
Оператор викону╓ весь рядок, вказаний м╕ж двома зворотн╕ми апострофами у командн╕й оболонц╕ /bin/sh або анало╜╕чн╕й до не╖ ╕ поверта╓ весь STDOUT назад до Перла. Як, напевне вже пом╕тив Допитливий Читач, тут н╕чого не говориться про STDERR , ╕ в╕рно - цей оператор просто ╕гнору╓ STDERR , тому якщо Ви д╕йсно хочете мати також ╕ STDERR у сво╓му скрипт╕, то Ви сам╕ ма╓те виконати певн╕ д╕╕ для цього. Дал╕ ми розглянемо, що саме потр╕бно для цього робити. А поки-що розберемся з тим, як нам використовувати вив╕д в╕д команди.
Вигляд, у якому дан╕ повертаються назад до Перлу залежить перш за все в╕д того, як використову╓ться оператор ``. Найпрост╕ший вар╕ант - Вам начхати на те, що саме поверта╓ цей оператор. Тобто, це може бути тод╕, коли Ви на 100% впевнен╕ в тому, що оператор викона╓ться без помилок ╕ Вас д╕йсно не хвилю╓, що може видати цей оператор.
Але ми, все-таки, розглянемо, як воно працю╓...
# touch myprog.log
# -rw-r--r-- 1 root root 0 Jan 6 18:55 myprog.log
# perl -e "`mv /var/log/myprog.log /var/log/myprog.log.0`"
# -rw-r--r-- 1 root root 0 Jan 6 18:55 myprog.log.0
Цей простенький shell-д╕алог демонстру╓ найпрост╕ше використання ``. (Хоча для такого використання, мабуть ╕ не варто було б користуватися Перлом, але ми робимо це для навчання). Перл╕вський оператор в цьому випадку просто зм╕ню╓ назву файлу за допомогою команди mv (1) . Наступний скрипт робить т╕ж сам╕ д╕╖, але в╕н пере╕менову╓ к╕лька ре╓страц╕йних ( log ) файл╕в. Скрипт зд╕йсню╓ так звану ротац╕ю ло╜-файл╕в, яка використову╓ться дуже часто практично в ус╕х сучасних Юн╕ксах.
#!/usr/bin/perl
`mv /var/log/myprog.log.3 /var/log/myprog.log.4`;
`mv /var/log/myprog.log.2 /var/log/myprog.log.3`;
`mv /var/log/myprog.log.1 /var/log/myprog.log.2`;
`mv /var/log/myprog.log.0 /var/log/myprog.log.1`;
`mv /var/log/myprog.log /var/log/myprog.log.0`;
Розглянемо такий дуже простий скрипт:
#!/usr/bin/perl
while (<>) { print }
Що в╕н робить? Та майже н╕чого... Тобто майже н╕чого корисного в план╕ практичного його використання. Але зате в╕н нам допоможе роз╕братися з тим, що таке STDIN
Meta:
Чи пробували ви коли-небудь виправляти помилки в надрукованому на друкарськ╕й машинц╕ текст╕? Н╕чого складного в ц╕й процедур╕ нема╓. Берете пляшечку з чудовою б╕лою р╕диною ╕ авторучку. Акуратно замальову╓те р╕диною вс╕ т╕ м╕сця, де в слов╕ "головнокомандуючий" пропущен╕ скр╕зь одн╕ й т╕ ж сам╕ дв╕ л╕тери, а ручкою допису╓те те, що потр╕бно. Н╕чого складного! Справд╕? Але, що якщо це злополучне слово трапля╓ться в текст╕ 156 раз╕в? П╕д к╕нець ц╕╓╖ процедури ви вже забудете, як╕ л╕тери вам треба вписувати. Саме для тако╖ роботи ╕ були створен╕ текстов╕ редактори. Друку╓те в командному рядку emacs ╕ запуска╓те автоматичний пошук ╕ зам╕ну, ╕ вже через 2 хвилини можете в╕днести в╕ддрукований зв╕т в штаб округу.
Складн╕ше, якщо ви працю╓те не в в╕йськов╕й комендатур╕. Помилки в цих випадках бувають б╕льш р╕зноман╕тними, ╕ вам треба прикласти значн╕ зусилля щоб ╖х виправити. Скаж╕мо таке: вс╕ назви м╕сяц╕в в текст╕ повинн╕ бути написан╕ не скороченими, а вони написан╕ як завгодно - ╕ "С╕чень", ╕ "С╕ч.", ╕ нав╕ть "С╕ч". Почнете зм╕нювати вс╕ "С╕ч" на "С╕чень" ╕ "С╕чень" п╕сля тако╖ операц╕╖ перетвориться у вас на "С╕ченьень". Як бути? Тут прийдуть на пом╕ч регулярн╕ вирази. В нашому випадку потр╕бно зам╕нити на "С╕чень" вс╕ випадки слова "С╕ч", в якому п╕сля "ч" ╕де або пропуск " ", або крапка ".", ╕ ╕гнорувати випадки коли п╕сля "ч" ╕де "е". Оце ╕ ╓ зразок регулярного виразу. (До реч╕, дуже простого регулярного виразу. Реальне життя приносить задач╕ набагато складн╕ш╕, н╕ж ця.)
Тим, хто хоч раз працював з командним рядком (будь-де чи то в ДОС╕, чи у Юн╕кс╕), регулярн╕ вирази знайом╕. П╕дстановка з╕рочки (*) та знаку запитання (?) ╓ зразком найпрост╕ших регулярних вираз╕в. В командн╕й оболонц╕ Юн╕кса (практично в будь-як╕й з них) з╕рочка означа╓ "будь-яка посл╕довн╕сть будь-яких символ╕в", а знак запитання означа╓ один будь-який символ. Тому, якщо вам, наприклад, потр╕бно д╕знатися назви вс╕х файл╕в, що починаються на "l", ви можете дуже просто надрукувати: ls l*
Теж саме стосу╓ться ╕ зак╕нчень файл╕в. Щоб д╕знатися, як╕ файли зак╕нчуються на "z": ls *z (з одн╕╓ю в╕дчутною р╕зницею м╕ж ДОСом та Юн╕ксом: останн╕й приклад в ДОС╕ не працю╓. В ДОС╕ з╕рочка означа╓ фактично "будь-яка посл╕довн╕сть л╕тер в╕д цього м╕сця до к╕нця рядка").
Тож, зверн╕мося тепер до найпрост╕ших регулярних вираз╕в у Перл╕.
Под╕бно до з╕рочки та знаку запитання в командн╕й оболонц╕, в Перл╕ ╓ так╕ ж вирази для визначення "будь-якого одного символа" та "будь-яко╖ посл╕довност╕ символ╕в". Але в Перл╕ це робиться ╕накше - будь-який символ познача╓ться крапкою ".", а п╕сля крапки ставиться так-би мовити "множник". В найпрост╕шому випадку таким множником ╓ таж сама з╕рочка, що ╕ в командн╕й оболонц╕, ╕ тут вона означа╓ те ж саме - "будь-яка к╕льк╕сть". Тобто, сказане в цьому абзац╕ означа╓, що ".*" визнача╓ будь-яку посл╕довн╕сть будь-яких л╕тер.
Перш, н╕ж ╕ти дал╕ давайте розберемося як користуватися регулярними виразами в Перл╕.
Можуть бути р╕зн╕ застосування для них, але, мабуть, два основних це так╕: по-перше, ви захочете д╕знатися чи входить той чи ╕нший рядок, який опису╓ться регулярним виразом у текст (програма ма╓ крикнути: "Знайшов!", коли натрапить на такий вираз) ╕, по-друге, знайшовши вираз, ви схочете зам╕нити його на щось таке, що вам б╕льше до вподоби.
Наступний шматок коду демонстру╓ наше перше застосування регулярних вираз╕в:
#!/usr/bin/perl
#### A.
$a = "anyky ";
if ($a =~ m/a.*/) { print $a; }
#### B.
$b = "benyky ";
if ($b =~ m/a.*/) { print $b; }
#### C.
$c ="iily varenyky ";
if ($c =~ m/a.*/) { print $c; }
А результат його виконання такий:
dk@sophy $ ./mytest.pl
anyky iily varenyky dk@sophy $
Давайте спробу╓мо роз╕братися з анатом╕╓ю цього коду. Анатом╕я - це, як в╕домо, копання у внутрощах. Давайте ╕ ми спробу╓мо роз╕брати один ╕з регулярних вираз╕в ╕ в╕дд╕лити в ньому мухи окремо, а котлети окремо. Для конкретност╕ будемо розглядати один з трьох вираз╕в, а саме той, що йде на початку:
if ($a =~ m/a.*/) { print $a; }
Тим, хто знайомий з програмуванням, безперечно здасться знайомою форма if (...) {...} , але ж наша книжка для непрограм╕ст╕в. Тому невеличке пояснення: if разом з двома групами дужок - круглими та ф╕гурними становить одну форму. Ця форма носить назву умовного оператора, ╕ з назви ╖╖ повинно бути зрозум╕лим, що, перш, н╕ж виконувати якусь д╕ю, умовний оператор перев╕ря╓ певну умову - чи треба цю д╕ю виконувати взагал╕, чи можна ╖╖ спок╕йно про╕╜норувати. В нашому приклад╕ ми користу╓мося найпрост╕шою формою умовного оператора. Ця форма ма╓ такий вигляд:
if ( <умова> ) { <блок> }
Якщо умова записана в круглих дужках справедлива, то тод╕ викону╓ться блок оператор╕в, записаний у ф╕гурних дужках п╕сля цього. Блок, який запису╓ться у ф╕╜урних дужках п╕сля умови часто називають т╕лом умовного оператора.
Умовою виконання блоку ╓ справедлив╕сть чи несправедлив╕сть операц╕╖ над регулярним виразом в круглих дужках. Якщо операц╕я да╓ в результат╕ "╕стину", то викону╓ться "<блок>" ( в нашому випадку оператор 'print'). Якщо ж в результат╕ ╕стина не виходить, то ╕ 'print' не викону╓ться.
Операц╕я, яка зд╕йсню╓ться над регулярним виразом, в свою чергу склада╓ться з трьох частин. Ц╕ частини так╕ (розгляда╓мо випадок A.):
л╕ва частина - "$a",
середня частина - "=~",
права частина - "m/a.*/".
Середня частина в цьому вираз╕ ╓ оператором, який з'╓дну╓ скалярний вираз, вказаний в л╕в╕й частин╕ з операц╕╓ю над регулярним виразом, вказаним в прав╕й частин╕.
Саме права частина виразу ╕ ╓ одним з тих регулярних вираз╕в, про як╕ ми зараз говоримо. Але сам регулярний вираз ╓ т╕льки частиною ц╕╓╖ третьо╖ (право╖) частини. В нашому конкретному випадку регулярним виразом ╓ "a.*". ╤ означа╓ цей вираз: "л╕тера a, п╕сля яко╖ йде будь-яка к╕льк╕сть будь-яких л╕тер".
Операц╕я над регулярним виразом познача╓ться л╕терою "m" та двома "н╕бито-дужками", в як╕ заключено регулярний вираз. Тобто:
"<операц╕я>/<регулярний-вираз>/".
Л╕тера "m" познача╓ операц╕ю пошуку (або пор╕вняння) в регулярному вираз╕. Походить ця л╕тера в╕д слова "match". Перл, побачивши такий вираз проводить пор╕вняння зразку тексту (який сто╖ть л╕воруч в╕д оператора "~=") ╕з регулярним виразом, який сто╖ть праворуч в╕д оператора. Якщо знайдена в╕дпов╕дн╕сть, то Перл кричить (ненашою мовою): "Match!" ╕ швиденько кида╓ться друкувати те, що ╕де у ф╕╜урних дужках.
Якщо спробувати оце "match" виразити укра╖нською, то найб╕льше п╕д╕йде слово: "Знайшов!". Саме оце "знайшов" виража╓ тут результат "╕стина". Протилежний до нього результат виража╓ться в укра╖нськ╕й як: "Та не переживай ти! Всяке бува╓!". А коли "всяке бува╓", оператор 'print' не викону╓ться.
Поглянувши пильн╕ше на результат виконання скрипту з регулярними виразами, пом╕тите, що виконалися два блоки цього скрипту, а саме: блок A. та блок C. Дв╕ зм╕нн╕ $a та $c мають в соб╕ л╕теру "a". У зм╕нн╕й $b ╖╖ нема╓, тому ╕ оператор print не виконувався в цьому блоц╕.
Перл широко в╕домий серед програм╕ст╕в сво╖ми скороченнями та спрощеннями. Один ╕ той же регулярний вираз в Перл╕ можливо записати багатьма р╕зними способами (кожен наступний з них коротший в╕д попереднього). ╤нколи така лакон╕чн╕сть приводить до спрощення написання програми у Перл╕, ╕нколи - навпаки, але практично завжди це приводить до того, що програму в Перл╕ неможливо прочитати.
В цьому п╕дрозд╕л╕ ми спробу╓мо скоротити ╕ так короткий скрипт, який використовувався в попередньому п╕дрозд╕л╕ (звичайно ж намагаючись робити так, щоб в╕н все-таки продовжував працювати).
Таку надзвичайно складну штуку, як одна л╕тера, в Перл╕ можна зам╕нити прост╕шою штукою - н╕чим. Два таких вирази тотожн╕ в Перл╕: m/a.*/ та /a.*/ . А якщо два вирази тотожн╕, то зрозум╕ло, що кожна нормальна людина з двох вираз╕в, як╕ приводять до одного й того ж результату, вибере той вираз, який коротший. ╤ це ╓ причиною того, що в б╕льшост╕ скрипт╕в ви будете бачити " " ╕ не побачите "m ".
З урахуванням цього наш скрипт перетворю╓ться на ось таке:
#!/usr/bin/perl
$a = "anyky ";
if ($a =~ /a.*/) { print $a; }
$b = "benyky ";
if ($b =~ /a.*/) { print $b; }
$c ="iily varenyky ";
if ($c =~ /a.*/) { print $c; }
Прост╕ше? Так. Але не набагато. Йдемо дал╕...
╤нколи бува╓ не дуже зручно користуватися стандартними "дужками", в як╕ береться регулярний вираз - "//". Наприклад, якщо ви робите пошук в текст╕ блок╕в ╕з косими всередин╕. Для того, щоб ╕золювати ц╕ кос╕ в╕д механ╕зму пошуку ╕снують р╕зн╕ засоби. Але коли таких "╕золятор╕в" занадто багато, бува╓ так, що регулярний вираз перетворю╓ться на щось таке, що прочитати неможливо. ╤ у таких випадках було б непагано мати щось на зам╕ну для стандартних дужок.
Такою зам╕ною ╓ ... практично будь що. Але т╕льки в тому випадку, коли використову╓ться форма m// (л╕тера m перед дужками обов'язкова). Зам╕ною для // може бути будь-який не алфав╕тно-цифровий ╕ не нульовий символ. Вс╕ наступн╕ вирази будуть тотожними:
if ($a =~ m/a.*/) { print $a; }
if ($a =~ m:a.*:) { print $a; }
if ($a =~ m~a.*~) { print $a; }
Кр╕м цього ма╓ться ще один вар╕ант запису цього ж самого виразу, в якому використовуються ф╕╜урн╕ дужки:
if ($a =~ m{a.*}) { print $a; }
В попередн╕х абзацах ми конкретно вказували з якою зм╕нною ведеться пор╕вняння регулярного виразу. Але в дуже багатьох випадках ц╕ конкретн╕ зм╕нн╕ можна зам╕нити на одну спец╕ально передбачену для цього зм╕нну, яка ╕ служить спец╕ально для того, щоб провадити р╕зн╕ операц╕╖ над текстовими даними ╕ регулярними виразами. Ця зм╕нна в Перл╕ запису╓ться, як $_ ╕ вжива╓ться наступним чином.
Ц╕й зм╕нн╕й можна присвоювати будь-як╕ значення, так саме, як будь-як╕й ╕нш╕й "нормальн╕й" зм╕нн╕й. Але, в багатьох випадках, коли зм╕нна $_ використову╓ться в операц╕ях, на не╖ можна не вказувати ссилку (тобто, прост╕ше кажучи ╖╖ можна не називати) ╕, кр╕м того, сам знак операнду може опускатися так саме.
З усим цим сказаним, наш попередн╕й приклад прийма╓ зовс╕м лакон╕чну форму:
#!/usr/bin/perl
$_ = "anyky ";
if (/a.*/) { print }
$_ ="benyky ";
if (/a.*/) { print }
$_="iily varenyky ";
if (/a.*/) { print }
Розглянемо ближче в цьому приклад╕ два наступн╕ рядки:
$_ = "anyky ";
if (/a.*/) { print }
В першому рядку зм╕нн╕й $_ присвою╓ться якесь значення. З цим все зрозум╕ло, ╕ питань тут нема╓. Трохи складн╕ше з другим рядком. У ф╕╜урних дужках сто╖ть просто 'print'. Саме час запитати: "Print - що?". ╤ справд╕ - що?
Оператор print, п╕дпада╓ п╕д туж саму конвенц╕ю, що ╕ б╕льш╕сть оператор╕в Перла. А саме: якщо в╕н не знаходить явно вказаних даних, над якими в╕н повинен працювати (друкувати - у випадку з print), в╕н друку╓ стандартну зм╕нну $_.
Теж саме стосу╓ться ╕ л╕во╖ частиною виразу - коли в круглих дужках вказаний сам т╕льки регулярний вираз ╕ не вказано текстово╖ зм╕нно╖, цей регулярний вираз пор╕вню╓ться ╕з стандартною зм╕нною $_.
Якщо виразити те, що написане в рядку if (/a.*/) { print } ╕ перекласти це з мови Перл на мову людську то вийде таке: "Якщо стандартна текстова зм╕нна $_ м╕стить в соб╕ л╕теру a п╕сля яко╖ йде будь-що, то треба цю стандартну зм╕нну надрукувати".
Що робити, якщо нам навпаки потр╕бно вибрати текстов╕ рядки, в яких не зустр╕ча╓ться той чи ╕нший регулярний вираз? Чи потр╕бно для цього писати якийсь спец╕льний вираз, чи може користуватися ╕ншою формою умовного оператора if ? Чи ще що-небудь?
Можна користуватися будь-яким з названих метод╕в. Але спец╕ально для таких ситуац╕й ╕сну╓ ╕нша форма пор╕вняння. Зам╕сть =~ потр╕бно користуватися оператором !~ , ╕ ал╜оритм роботи команди при цьому зм╕ню╓ться на прямо протилежний: блок, що йде в т╕л╕ умовного оператора викону╓ться т╕льки тод╕, коли не викону╓ться д╕я над регулярним виразом.
Тобто:
#!/usr/bin/perl
$a = "anyky ";
if ($a !~ /a.*/) { print $a; }
$b = "benyky ";
if ($b !~ /a.*/) { print $b; }
$c ="iily varenyky ";
if ($c !~ /a.*/) { print $c; }
да╓ такий результат:
./mytest.pl
benyky
Знак оклику всередин╕ круглих дужок оператора if також зм╕ню╓ значення умовного оператора на протилежне. Тобто, т╕льки що приведений скрипт буде анало╜╕чним до наступного:
#!/usr/bin/perl
$_ = "anyky ";
if (!/a.*/) { print }
$_ ="benyky ";
if (!/a.*/) { print }
$_="iily varenyky ";
if (!/a.*/) { print }
╤ нарешт╕ - ще одна форма умовного оператора. Наск╕льки мен╕ в╕домо - Перл - це ╓дина мова, яка в даний момент ма╓ таку форму умовного оператора. В Перл╕ оператор unless ╓ зворотн╕м до оператора if . Тобто, if (!<умова>) {<д╕я>} ╓ анало╜╕чним до unless (<умова>) {<д╕я>} , ╕ з урахуванням т╕льки що сказаного попередн╕й скрипт можна переписати у вигляд╕:
#!/usr/bin/perl
$_ = "anyky ";
unless (/a.*/) { print }
$_ ="benyky ";
unless (/a.*/) { print }
$_="iily varenyky ";
unless (/a.*/) { print }
Що-ж, п╕сля того, як ми вже трохи познайомилися ╕ ╕з самими регулярними виразами, ╕ ╕з деякими ╖х застосуваннями, мабуть час вже просто витягти руки з кишень, простягнути ╖х один одному ╕ формально представитися ╕ роззнайомитися ближче з подробицями. Але ж, звичайно, п╕д час таких формальних знайомств про себе багато не кажуть. Т╕льки й того що, такий-то такий-то, працюю там-то, а роблю... Та ладно, чого там!
Тож, наше оф╕ц╕йне знайомство з регулярними виразами теж буде не занадто фам╕льярним. Досить коротк╕ в╕домост╕ про синтаксис, к╕лька коротеньких приклад╕в, тощо. Тож, будьмо!
А. Ейнштейн
^ - означа╓ початок рядка, якщо сто╖ть на початку регулярного
виразу. ╤нше застосування ми розглянемо п╕зн╕ше.
$ - означа╓ к╕нець рядка (звичайно ж, анало╜╕чно до
попереднього випадку, т╕льки якщо сто╖ть в к╕нц╕
регулярного виразу).
Як об╕цяно, к╕лька коротеньких приклад╕в.
Приклад 1.
$_="anyky";
if (/^a.*/) { print }
$_ ="benyky ";
if (/^a.*/) { print }
$_=" varenyky";
if (/^a.*/) { print }
да╓ в результат╕:
./mytest.pl
anyky
Зрозум╕ло, бо т╕льки в першому вираз╕ a сто╖ть на початку рядка.
Приклад 2.
А в такому вар╕ант╕
$_="anyky";
if (/.*y$/) { print }
$_ ="benyky ";
if (/.*y$/) { print }
$_=" varenyky";
if (/.*y$/) { print }
ми отриму╓мо таке:
./mytest.pl
anyky varenyky
(Зверн╕ть увагу на пропуск, що ╕де в к╕нц╕ другого виразу, ╕ стане зрозум╕ло, чому не надруковано другий вираз.)
Приклад 3.
Якщо об'╓днати початок рядка та к╕нець рядка, то отрима╓мо таке:
$_="anyky";
if (/^a.*y$/) { print }
$_ ="benyky ";
if (/^a.*y$/) { print }
$_=" varenyky";
if (/^a.*y$/) { print }
./mytest.pl
anyky
Приклад 4.
Як знайти пустий рядок? Дуже просто! Пустий рядок склада╓ться з початку ╕ к╕нця, ╕ н╕чого посередин╕:
$_="";
if (/^$/) { print "Пустий рядок!" }
./mytest.pl
Пустий рядок!
╤ невеличка вправа п╕д к╕нець:
Вправа
Попробуйте наперед сказати, що надрукують так╕ оператори?
$_="слово";
if (/^.*$/) { print "Рядок не пустий!" }
$_="";
if (/^.*$/) { print "Рядок не пустий!" }
Напиш╕ть невеличкий скрипт з цим оператором ╕ перев╕рте, чи в╕рне було ваше передбачення.
Регулярн╕ вирази в Перл╕ мають певну к╕льк╕сть символ╕в, як╕ мають дещо спец╕альне трактування. Ми вже знайом╕ з к╕лькома з них, це крапка, з╕рочка, стр╕лка вгору ( ^ ) та знак долара ( $ ). ╢ ще деяк╕ спец╕альн╕ символи кр╕м цих, але ╖х не дуже багато. За виключенням цього досить невеликого набору символ╕в, вс╕ ╕нш╕ трактуються в Перл╕ досить просто - вони означають сам╕ себе. Так, як, наприклад, л╕тера a , у вираз╕ if (^a) означа╓ саме це - л╕теру a .
Тобто, б╕льш╕сть алфав╕тно-цифрових символ╕в у регулярних виразах Перла не мають якогось особливого значення ╕ перше правило буде таке:
будь-який символ за - означа╓ сам цей символ.
виключенням спец╕альних
символ╕в
Але дал╕ якраз ╕ почина╓ться розмова про множники. Вони потр╕бн╕, коли треба в╕дшукати рядки, в яких та чи ╕нша л╕тера трапля╓ться не один раз (як в if (/a/) ), або два рази ( як в if (/aa/) ), а певну (наперед задану або, нав╕ть, ╕ нев╕дому наперед) к╕льк╕сть раз╕в.
До цього моменту ми користувалися ╓диним модиф╕катором (множником) коли треба було в╕дшукати дов╕льну к╕льк╕сть л╕тер у вираз╕. Це була комб╕нац╕я ╕з крапки та з╕рочки " .* ". Трапляються ╕ ╕нш╕ ситуац╕╖ - треба знайти рядки, в яких л╕тера "зю" зуст╕ча╓ться 5 (не б╕льше, ╕ не менше) раз╕в, або коли в╕дм╕тити т╕ речення, в яких сто╖ть по три коми п╕дряд (Нав╕ть ╕ дв╕ п╕дряд буде вже багато, тому може виникнути задача в╕дшукати вс╕ м╕сця, де сто╖ть б╕льше одн╕╓╖ коми, ╕ зм╕нити ╖х ... ну, скаж╕мо на знак запитання). Саме в таких випадках потр╕бно вдаватися до "множник╕в" -- множаться л╕тери на рази. Вс╕ наступн╕ вирази вживаються, коли потр╕бно в╕дшукати (по-перл╕вськи кажучи "match") певний символ, який трапля╓ться певну к╕льк╕сть раз╕в. ╤ вс╕ ц╕ вирази вживаються так:
<символ><множник>
Де в якост╕ множник╕в вживаються так╕ реч╕ (в наступних визначеннях N ╕ M - це ц╕л╕ числа):
"*" - вже в╕дома нам з╕рочка, яка буквально означа╓
"будь-яка к╕льк╕сть раз╕в" (включаючи нуль);
+ - дуже схожий за вжитком до з╕рочки *, але якщо
з╕рочка означа╓ "будь-яку к╕льк╕сть раз╕в", то
+ означа╓ "один, або б╕льше раз╕в";
{N} - означа╓ "N раз╕в".
{N,M} - означа╓ "в╕д N до M раз╕в".
{N,} - означа╓ "N або б╕льше раз╕в".
{,M} - означа╓ "не б╕льше, н╕ж M раз╕в".
N.B.
Зауважте, що п╕д к╕льк╕стю раз╕в в ус╕х перечислених
випадках ма╓ться на уваз╕ к╕льк╕сть раз╕в ск╕льки та чи ╕нша
л╕тера сто╖ть п╕дряд в текстовому рядку, а не загальна
к╕льк╕сть л╕тер в рядку.
╤ зразу-ж не в╕дкладаючи на завтра приклади:
Приклад 1
Вираз /b+/ буде в╕дпов╕дати таким текстовим фра╜ментам:
, але не буде в╕дпов╕дати такому як
Приклад 2
Вираз /b*/ в╕дпов╕да╓ вс╕м виразам з попереднього прикладу.
Приклад 3
/o{2}/ в╕дпов╕да╓ т╕льки одному ╓диному виразу: "abooboo", а /b{2}/ в╕дпов╕да╓ т╕льки "abba".
Приклад 4
Регулярний вираз /a+/ ╓ тотожним таким регулярним виразам, як /a{1,}/ та /aa*/ .
Друге правило формулю╓ться таким чином: все, що сто╖ть в квадратних дужках, розгляда╓ться Перлом як виб╕р. - п╕дходить будь-який символ ╕з тих, що вказан╕ в цьому перел╕ку. Тобто, [ab] означа╓ "або a , або b ".
Людин╕, як╕й потр╕бно в╕дшукати в текст╕ схож╕ слова, як наприклад, "трава" та "дрова" потр╕бно було-б написати таке:
if (/[тд]р[оа]ва/) { print }
(Але зверн╕ть увагу, що п╕д цю категор╕ю п╕дпадають також так╕ вар╕анти, як "драва" та "трова".
Приклад 5
Якщо Вам потр╕бно ╕з списку дат, надрукувати т╕льки т╕, що в╕дносяться або до 1998 або до 1999 року, Ви можете написати таке:
if (/199[89]/) { print }
Деяким розширенням до цього правила ╓ введення спец╕альних символ╕в усередин╕ квадратних дужок. Щоб не переписувати весь п╕дряд алфав╕т, коли Вам просто потр╕бно сказати "Будь-яка л╕тера", Ви просто можете записати, так, як би вираз "В╕д а до я" Вам треба було б записати на шматку паперу: "а-я". Виглядатиме це дуже просто:
if (/a-z/) { print }
Що просто означа╓, що буде надрукаваний кожен рядок, в якому трапля╓ться будь-яка л╕тера (маленька !) англ╕йсько╖ абетки. Тобто:
Приклад 6
Вищенаведений регулярний вираз в╕дпов╕да╓ таким текстовим рядкам:
╕ не буде в╕дпов╕дати жодному з наведених нижче:
Теж саме стосу╓ться ╕ цифр: будь-яка десяткова цифра, що прийма╓ значення в╕д 3 до 8 може бути записана як [3-7] .
╤ вже, фактично, в багатьох наведених прикладах ми сформулювали трет╓ правило : прост╕ш╕ регулярн╕ вирази комб╕нуються ╕ утворюють б╕льш складн╕ вирази.
Приклад 7
Регулярний вираз /^ 199[7-9] [a-z]+/ склада╓ться фактично з п'яти "атомарних" регулярних вираз╕в:
Наступн╕ рядки вс╕ будуть в╕дпв╕дати цьому регулярному виразу:
+-------------------------
| 1998 omega
| 1999 alpha
| 1997 beta
| 1997 gamma $20
| 1998 sigma Proxima
| 1999 s $40
|
, а наступн╕ - не п╕дходять п╕д це визначення:
+-------------------------
|1999 omega
|1998alpha
|1997 Beta
|1999 $100
| 1999 omega
|
Невеличк╕ вправи, що виплива╓ з цього прикладу:
До цього ми мали т╕льки одне застосування регулярних вираз╕в, а саме - надрукувати рядок, якщо регулярний вираз справдився. Але, саме собою зрозум╕ле, що цього може виявитися занадто мало. Дуже часто потр╕бно бува╓ виконати як╕сь д╕╖ саме над т╕╓ю частиною рядка, яка задовольня╓ певному регулярному виразу.
В цьому випадку на допомогу приходять дужки. Застосування круглих дужок да╓ можлив╕сть ╕з ц╕лого регулярного виразу вид╕лити певну його частину, яка присвою╓ться п╕сля перев╕рки спец╕альн╕й зм╕нн╕й. Над ц╕╓ю зм╕нною можна п╕зн╕ше виконувати будь-як╕ д╕╖, дозволен╕ над зм╕нними. Так╕ зм╕нн╕ мають спец╕альний вигляд ╕ позначаються одним ╕з двох способ╕в: або зворотньою косою, за якою ╕де цифра в╕д 1 до 9 (наприклад, \1, \5), або знаком долара за яким ╕де натуральне число (як наприклад, $1, $5 або $29). Кожна така зм╕нна ($n або \n) означа╓ ту частину рядка, що поставлена у в╕дпов╕дн╕сть регулярному виразу, який записаний у n-х зл╕ва дужках.
Приклад 1
В╕зьмемо регулярний вираз ╕з попереднього параграфу ╕ трохи його зм╕нимо. Нехай деяка зм╕нна прийма╓ по черз╕ так╕ значення:
$a = " 1998 omega";
$a = " 1999 alpha";
$a = " 1997 beta"
$a = " 1997 gamma $20"
$a = " 1998 sigma Proxima";
$a = " 1999 s $40";
$a = " 1999 omega";
$a = " 1998alpha";
$a = " 1997 Beta";
$a = " 1999 $100";
$a = " 1999 omega";
До всих цих зм╕нних ми застосу╓мо такий оператор:
if ($a =~ /^ (199[7-9]) ([a-z]+)/) {
$year = $1;
$item = $2;
print "Р╕к: ", $year," пункт: ", $item, "\n";
}
Тод╕ результатом роботи цього скрипту буде такий вих╕д:
Р╕к: 1998 пункт: omega
Р╕к: 1999 пункт: alpha
Р╕к: 1997 пункт: beta
Р╕к: 1997 пункт: gamma
Р╕к: 1998 пункт: sigma
Р╕к: 1999 пункт: s
Приклад 2
Перша робоча програмка, яка може хоч на що-небудь згодитись. Давайте спробу╓мо написати скрипт, який буде працювати з реальними даними в будь-якому Юн╕кс╕. Увага! Це Юн╕кс специф╕чний скрипт, ╕ в╕н не ма╓ аж н╕якого сенсу у ╕нших системах типу В╕ндовс, ДОС, тощо. "Юн╕кс" тут використову╓ться у "широкому" смисл╕ - Л╕накс теж попада╓ п╕д це означення.
З часом, осво╖вши Перл трохи б╕льше, Ви пом╕тите, що цей же скрипт можна написати набагато прост╕ше. Але ми напишемо його, використовуючи т╕льки т╕ засоби, що ми вже вивчили до цього моменту. Тому в ньому можлив╕ деяк╕ "важк╕" конструкц╕╖.
Але нам все-ж таки не об╕йтись в╕д к╕лькох нових речей. Це реч╕ так╕:
Наш скрипт буде такий:
#!/usr/bin/perl
if (open (STDIN, "who|")) {
while (<>) {
if (/^([a-z][a-z0-9]*) /) {
print $1, "\n"
}
}
} else {
die "Не можу створити процес who !\n";
}
Уяв╕ть соб╕, що Вам потр╕бно знати, хто працю╓ на даний момент у Ваш╕й систем╕. У Юн╕кс╕ про це можна д╕знатися, виконавши команду who .
META ((( non greedy вирази з -- ? )))
META ((( дужки ╕ $<цифра> )))
* scope of $(digit)
* difference \ $ (more: \11, $11, \011)
* othe matches $+, $&, $`, $', $0
META ((( приклад бази даних - телефонний дов╕дник )))
====
META : (спец╕альн╕ символи (metacharacters) - egrep )
\ Quote the next metacharacter
^ Match the beginning of the line
. Match any character (except newline)
$ Match the end of the line (or before newline at the end)
| Alternation
() Grouping
[] Character class
META : (спец╕альн╕ символи - розширення до egrep )
\t tab (HT, TAB)
\n newline (LF, NL)
\r return (CR)
\f form feed (FF)
\a alarm (bell) (BEL)
\e escape (think troff) (ESC)
\033 octal char (think of a PDP-11)
\x1B hex char
\c[ control char
\l lowercase next char (think vi)
\u uppercase next char (think vi)
\L lowercase till \E (think vi)
\U uppercase till \E (think vi)
\E end case modification (think vi)
\Q quote (disable) pattern metacharacters till \E