Программирование на Linux

Программирование на Linux

Говоря о командной строке, на самом деле мы имеем в виду командную оболоч­ку (shell) . Командная оболочка — это программа, которая принимает команды, введенные с клавиатуры, и передает их операционной системе для выполнения Практически все дистрибутивы Linux поставляются с командной оболочкой из проекта GNU, которая называется bash. Имя bash — это аббревиатура от названия Bourne Again Shell, отражающего тот факт, что bash является улучшенной заменой sh, первоначальной командной оболочки для Unix, написанной Стивом Борном (Steve Bourne) . При использовании графического интерфейса для взаимодействия с командной оболочкой нам понадобится еще одна программа — эмулятор терминала. Загля­нув в меню рабочего стола, вы наверняка обнаружите такую программу. В KDE используется konsole, в GNOME — gnome-terminal, однако соответствующий пункт в меню часто называется просто «terminal» (или «терминал»). Для Linux существует также множество других эмуляторов терминала, но все они решают одну и ту же задачу: предоставляют доступ к командной оболочке Со временем у вас наверняка появятся свои предпочтения, в зависимости от «рюшечек и бан­тиков», которые они имеют Итак, приступим. Запустите эмулятор терминала! После появления окна на экра­не вы увидите в нем нечто подобное: Это называется приглашением к вводу (shell prompt) и появляется всякий раз, когда командная оболочка готова принять ввод В разных дистрибутивах при­глашение выглядит по-разному, но обычно включает строку имя_пользователя@ имя_компьютера, за которой следует имя текущего каталога (подробнее об этом чуть ниже) и знак доллара Если последний символ в приглашении — знак решетки (#), а не знак доллара, это означает, что сеанс в терминале обладает привилегиями суперпользователя. То есть либо вы зарегистрировались как пользователь root, либо запустили эму­лятор терминала, который автоматически устанавливает привилегии суперполь­зователя (администратора) Будем считать, что пока все идет хорошо, и попробуем что-нибудь ввести Набе­рите на клавиатуре какую-нибудь бессмыслицу, например: Поскольку это бессмыслица, командная оболочка немедленно сообщит об этом и даст вам второй шанс: Для работы с командной оболочкой достаточно одной клавиатуры, однако эмулятор терминала позволяет также использовать мышь. X Window System (механизм, который воспроизводит графический интерфейс на экране) поддерживает прием быстрого ко­пирования с помощью мыши. Если выделить текст, нажав левую кнопку и переместив указатель мыши над ним (или выполнив двойной щелчок на слове), он будет скопирован в специальный буфер, которым управляет X. Нажатие средней кнопки мыши вызовет вставку текста в позицию курсора. Попробуйте проделать этот фокус. Не пытайтесь использовать комбинации CTRL+C и CTRL+V для выполнения копирования и вставки в окне терминала. Эти команды там не работают. В командной оболочке эти комбинации клавиш имеют другое значение, присвоенное им задолго до появления Microsoft Windows. Графическое окружение вашего рабочего стола (скорее всего, KDE или GNOME) рабо­тает очень похоже на графическое окружение Windows и, вероятнее всего, реализует политику «щелкни, чтобы передать фокус ввода». Это означает, что для передачи фокуса ввода в окно (его активизации) на нем нужно щелкнуть мышью. Это противо­речит традиционному поведению X «фокус следует за мышью», когда для передачи фокуса ввода в окно достаточно просто навести на него указатель мыши. Окно не под­нимется на передний план, пока вы не щелкнете на нем мышью, но способно принять фокус ввода. Настройка политики «фокус следует за мышью» упростит работу с окном терминала. Попробуйте, я думаю, вам понравится. Соответствующие параметры на­ходятся в программе настройки вашего диспетчера окон.

История команд в Linux

Если теперь нажать клавишу со стрелкой вверх, после приглашения к вводу по­явится предыдущая команда kaekfjaeifj. Это называется историей команд. Боль­шинство дистрибутивов Linux по умолчанию запоминают последние 500 команд. Нажмите клавишу со стрелкой вниз, и предыдущая команда исчезнет Вызовите предыдущую команду, еще раз нажав клавишу со стрелкой вверх. Те­перь попробуйте понажимать клавиши со стрелками влево и вправо Видите, как меняется позиция курсора в командной строке? Благодаря этому легко можно ре­дактировать, когда вы понажимали клавиши, попробуем ввести несколько простых команд. Первая команда date. Она выводит текущие время и дату: Файлы, имена которых начинаются с точки, считаются скрытыми. Это означает, что команда ls не будет выводить их, если не вызвать ее с параметром: ls - a. В момент создания учетной записи пользователя в его домашний каталог по­мещается несколько скрытых файлов, где хранятся различные параметры на­стройки учетной записи. Далее в этой книге мы еще вернемся к подобным файлам и посмотрим, как можно настроить свое окружение. Кроме того, некоторые приложения помещают в домашний каталог свои скрытые файлы с настройками. Linux, как это принято в Unix, различает регистр символов в именах файлов и командах. Файлы с именами Filel и filel — это разные файлы. В Linux не поддерживается понятие «расширения файла», как в некоторых других операционных системах. Вы можете давать своим файлам любые имена. Тип и/или назначение файла определяется другими средствами. Но даже при том, что Unix-подобные операционные системы не используют расширения файлов для определения типа/назначения файлов, некоторые прикладные программы все же используют их для этой цели. Хотя Linux поддерживает длинные имена файлов с пробелами и знаками пун­ктуации, старайтесь не использовать в именах файлов другие знаки пунктуации, кроме точки, дефиса и подчеркивания. Также не используйте пробелы в именах файлов. Наличие пробелов в именах файлов осложняет решение многих задач командной строки — вы это увидите Если необходимо отделить друг от друга слова в имени файла, используйте символы подчеркивания. Потом вы не раз скажете себе спасибо за это. Теперь, когда мы знаем, как перемещаться по файловой системе, совершим об­зорное путешествие по системе Linux Но прежде чем отправиться, познакомимся еще с несколькими командами, которые пригодятся в пути: Команда ls является, пожалуй, одной из самых часто используемых команд, и не без оснований С ее помощью можно увидеть, что находится в каталоге, и узнать некоторые важные атрибуты файлов и каталогов Как мы уже видели, чтобы по­лучить список файлов и подкаталогов в текущем рабочем каталоге, достаточно ввести команду ls: Мы подошли к очень важному моменту, касающемуся особенностей работы большинства команд Команды часто сопровождаются одним или несколькими параметрами, изменяющими их поведение, и дополнительными, одним или не­сколькими, аргументами, на которые воздействует команда Поэтому большин­ство команд выглядят примерно так: команда - параметры аргументы Большинство команд используют параметры, состоящие из одного символа, кото­рому предшествует дефис, например: Но многие команды, в том числе коман­ды из проекта GNU, поддерживают параметры с длинными именами, состоящие из слова, которому предшествуют два дефиса Кроме того, многие команды по­зволяют объединять вместе параметры с короткими именами. В следующем при­мере команде ls передаются два параметра: параметр l, требующий использовать длинный (long) формат вывода, и параметр t, требующий сортировать результаты по времени (time) изменения: Добавим параметр с длинным именем --reverse, чтобы изменить порядок сорти­ровки на обратный: Команда ls имеет огромное число допустимых параметров. Наиболее популяр­ные из них перечислены Как было показано выше, параметр - l заставляет команду ls выводить резуль­таты с использованием длинного формата. Этот формат предусматривает вывод большого количества полезной информации. Ниже приводится пример вывода содержимого каталога Examples в системе Ubuntu:файлу. Первый символ указывает тип файла. На­ пример, символом дефиса обозначаются обычные файлы, а сим­волом d — каталоги. Следующие три символа сообщают о правах доступа для владельца файла, следующие три — для членов группы, которой принадлежит файл, и последние три — для всех остальных. Более полное обсуждение прав доступа приводится

Определение типов файлов командой file

Занимаясь исследованием системы, полезно иметь возможность определять тип содержимого файлов. В этом нам поможет команда file. Как отмечалось выше, имена файлов в Linux не обязаны отражать тип содержимого файлов. Например, увидев имя файла picture. jpg, можно предположить, что он содержит изображение в формате JPEG, но в Linux такие предположения могут не оправдываться. Вы­звать команду file можно так:

Существует множество разных типов файлов. Одна из известных идей в Unix - подобных системах, таких как Linux, гласит: «Все сущее есть файл». По мере чте­ния книги вы убедитесь в истинности этого утверждения. Типы многих файлов в вашей системе будут вам знакомы, например файлы MP3 и JPEG, но иногда будут попадаться файлы с малоизвестными и даже странными типами Команда less — это программа для просмотра текстовых файлов. В системе Linux присутствует множество файлов, содержащих обычный, удобочитаемый текст Программа less предоставляет удобный способ исследовать их содержимое. Зачем может понадобиться исследовать текстовые файлы? Дело в том, что многие файлы с системными настройками (их называют конфигурационными файлами) хранят информацию в этом формате, что дает возможность прочитать их и вник­нуть в особенности работы системы Кроме того, в этом формате хранятся многие программы в системе (их называют сценариями) . В последних главах мы узнаем, как редактировать файлы с настройками системы и как писать свои сценарии, а пока просто просматривайте их содержимое. Существует множество способов представления информации в компьютере. Все спо­собы связаны с определением отношения между смысловой информацией и числами, которые применяются для ее представления. В конце концов, компьютеры могут работать только с числами, и все данные в компьютере преобразуются в числовое представление. Некоторые из этих систем представления очень сложны (например, сжатые видео­файлы), другие, напротив, очень просты. Одна из самых ранних и простых систем называется ASCII-текст. ASCII (произносится «ас-ки») — это аббревиатура названия «American Standard Code for Information Interchange» (американский стандартный код для обмена информацией). Эта простая система кодирования впервые была исполь­зована в телетайпах. Текст — это простое отображение «один в один» символов в числа. Это очень компакт­ный формат. Пятьдесят символов текста преобразуются в пятьдесят байт данных. Но это не то же самое, что текст в документе, созданном текстовым процессором, таким как Microsoft Word или OpenOffice. org Writer. Файлы документов, в отличие от простых файлов с ASCII-текстом, содержат множество нетекстовых элементов, используемых для описания их структуры и форматирования. Файлы с простым ASCII-текстом содержат только сами символы и очень небольшое количество простейших кодов управления, таких как символы табуляции, возврата каретки и перевода строки. В системе Linux многие файлы хранятся в текстовом формате, и многие инструменты работают с текстовыми файлами. Даже Windows признает важность этого формата. Хорошо известная программа Notepad (Блокнот) — это редактор для простых файлов с ASCII-текстом. Команда less используется так: less имя_файла После запуска программа less позволяет прокручивать текстовый файл взад и вперед Например, просмотреть содержимое файла со всеми известными систе­ме учетными записями пользователей можно с помощью следующей команды: После запуска программа less выведет содержимое файла. Если файл занимает больше одной страницы, его можно прокручивать вверх и вниз. Чтобы выйти из программы less, нажмите клавишу Q. Программа less создавалась как улучшенная замена более ранней Unix-программы с именем more. Ее имя — это игра слов «less is more» (меньше значит больше) — девиз архитекторов-модернистов и проектировщиков. less относится к категории программ постраничного просмотра текстовых документов, которые называют пейджерами (pagers). В отличие от программы more, которая может листать страницы только вперед, программа less способна листать текст в обоих на­правлениях, вперед и назад, и имеет множество других особенностей. Файловая система в Linux имеет практически ту же компоновку, что и в других Unix-подобных системах. Фактически ее структура определяется опублико­ванным стандартом с названием «Linux Filesystem Hierarchy Standard». Не все дистрибутивы Linux следуют этому стандарту, но большинство придерживаются его достаточно близко

Файловая система в Linux

А теперь немного попутешествуем по файловой системе и познакомимся с основ­ными достопримечательностями системы Linux. Это даст нам шанс попрактико­ваться в навигации Первое, что мы обнаружим: многие интересные файлы имеют простой текстовый формат В ходе путешествия пробуйте выполнить следующие действия: В ходе путешествия не бойтесь заглядывать внутрь системы. Обычные пользо­ватели практически ничего не смогут испортить Это работа системного админи­стратора! Если команда пожалуется на что-то, просто перейдите к чему-нибудь другому Потратьте некоторое время на знакомство с окрестностями Это наша система, и мы вправе заниматься ее исследованием Помните, что в Linux нет се­кретов! Просматривая содержимое каталогов, нередко можно увидеть такие записи: lrwxrwxrwx 1 root root 11 2012-08-11 07:34 libc. so.6 -> libc-2.6.so Обратили внимание на первую букву l и на присутствие двух имен файлов в кон­це? Это специальный файл, который называется символической ссылкой (иногда их называют мягкими ссылками или, на жаргоне, симлинками). В большинстве Unix-подобных систем можно дать одному и тому же файлу несколько имен. Даже при том, что на данный момент ценность такого приема может быть не очевидна, в действительности это очень удобная возможность. Вообразите следующий сценарий: программе требуется некий разделяемый ре­сурс (например, библиотека), хранящийся в файле с именем foo, но номер версии foo меняется очень часто. Было бы хорошо включить номер версии в имя файла, чтобы администратор или другое заинтересованное лицо могли видеть, какая вер­сия foo установлена. И здесь возникает проблема. Если изменить имя разделя­емого ресурса, нам придется проверять каждую программу, использующую этот ресурс, и изменять в ней имя ресурса на новое, после установки новой версии ре­сурса Если честно, такая перспектива не выглядит привлекательной Символические ссылки помогут спасти положение. Допустим, мы установили foo версии 2. 6 с именем файла foo-2.6 и затем создали символическую ссылку с про­стым именем foo, указывающую на ресурс foo-2.6. То есть когда программа откро­ет файл foo, в действительности она откроет файл foo-2.6. И все будут счастливы. Программы, полагающиеся на имя foo, найдут нужный файл, а мы сможем уви­деть фактическую версию ресурса. Когда придет время обновить ресурс до версии foo-2.7, мы просто добавим файл в систему, удалим символическую ссылку foo и создадим новую символическую ссылку, указывающую на новую версию. Та­кой подход не только решает проблему обновления версий, но также позволяет сохранить на компьютере обе версии ресурса. Представьте, что в версии foo-2.7 обнаружилась ошибка (ох уж эти разработчики!) и нужно вернуть старую версию. В этом случае достаточно просто вновь удалить символическую ссылку, указыва­ющую на новую версию, и создать новую символическую ссылку, указывающую на старую версию Запись выше (получена в каталоге /lib в системе Fedora) соответствует символической ссылке с именем libc. so.6, указывающей на файл разделяемой библиотеки с именем libc-2.6.so. Это означает, что программа, ищущая libc. so.6, фактически получит файл libc-2.6.so. Как создавать символические ссылки, мы узнаем в сле­дующей главе Пока мы не ушли далеко от темы ссылок, нужно упомянуть, что существует второй тип ссылок, которые называют жесткими ссылками (hard link). Жесткие ссылки так же позволяют присвоить одному файлу несколько имен, но они действуют иначе. Подробнее о различиях между жесткими и символическими ссылками рассказывается в следующей главе.

Операции с файлами и каталогами

Теперь мы готовы приступить к настоящей работе! В этой главе будут представле­ны следующие команды: Эти пять команд относятся к числу наиболее часто используемых в Linux Они применяются для управления файлами и каталогами Справедливости ради следует заметить, что некоторые задачи, выполняемые эти­ми командами, гораздо проще решаются с помощью графического диспетчера файлов В диспетчере файлов можно мышью перетаскивать файлы из одного ка­талога в другой, вырезать и вставлять файлы, удалять файлы и т. д. Но зачем тогда использовать эти старые программы командной строки? Ответ прост: потому что они обладают мощностью и гибкостью Несмотря на то что простые операции с файлами легко выполняются в диспетчере файлов с гра­фическим интерфейсом, сложные задачи проще решать с помощью программ ко­мандной строки. Например, как скопировать файлы HTML из одного каталога в другой, причем только те, что отсутствуют в каталоге назначения или имеют бо­лее позднюю дату последнего изменения? Сделать это в диспетчере файлов очень сложно, но легко в командной строке: cp - u. html destination Прежде чем приступать к использованию обсуждаемых команд, необходимо сна­чала поговорить об одной особенности командной оболочки, которая делает эти команды такими мощными. Так как имена файлов используются в командной обо­лочке повсеместно, она поддерживает специальные символы, помогающие быстро определять группы имен файлов. Эти специальные символы называют групповы­ми символами (wildcards) . Групповые символы (также известны как символы под­становки (globbing)) позволяют выбирать имена файлов по шаблону перечислены групповые символы и их соответствия Групповые символы можно использовать с любыми командами, принимающими имена файлов в виде Групповые символы имеют особую ценность не только потому, что часто используют­ся в командной строке, но и потому, что поддерживаются некоторыми диспетчерами с графическим интерфейсом. В Nautilus (диспетчер файлов для GNOME) можно выбирать файлы с помощью диалога Edit (Правка) ? Select Pattern (Выделить по шаблону). Просто введите шаблон для выбора файлов с групповыми символами, и в текущем каталоге будут выделены файлы, соответствующие шаблону. В некоторых версиях Dolphin и Konqueror (диспетчеры файлов для KDE) груп­повые символы можно вводить непосредственно в адресную строку. Например, если понадобится увидеть все файлы с именами, начинающимися с буквы «u» в нижнем регистре, в каталоге /usr/bin, просто введите в адресной строке текст: /usr/bin/u, и вы получите желаемый результат.

Большинство идей, первоначально реализованных в интерфейсе командной строки, перекочевали и в графический интерфейс. Это одно из множества обстоятельств, ко­торые делают настольный компьютер с Linux таким мощным инструментом. Команда mkdir используется для создания каталогов. Вызывается она следующим образом: Примечание к форме записи: В этой книге три точки в описании команды, следу­ющие за аргументом (как в примере выше), говорят о том, что аргументов может быть несколько; то есть в данном случае команда mkdir dir1 Команда cp копирует файлы и каталоги. Ее можно использовать двумя разными способами: cp item1 item2 чтобы скопировать один файл или каталог item1 в файл или каталог item2, и cp элемент... каталог чтобы скопировать несколько элементов (файлов или каталогов) в указанный ка­талог. Unix-подобные операционные системы, такие как Linux, не имеют команды, отменяющей удаление. Если вы что-то удалили командой rm, это исчезнет навсегда. Linux считает вас достаточно ответственным человеком, отдающим себе отчет в своих действиях. Будьте особенно осторожны с групповыми символами. Рассмотрим следующий класси­ческий пример. Допустим, вы захотели удалить все файлы HTML в каталоге. Для этого вы вводите команду: которая сделает именно то, что вам нужно, но если вы случайно вставите пробел между * и. html, как в следующей команде: rm удалит все файлы в каталоге и затем сообщит, что не нашла файла. html. Полезный совет: всякий раз, используя групповые символы с командой rm (помимо внимательной проверки ввода!), проверьте сначала аргумент с групповым символом с командой ls. Это позволит увидеть, какие файлы будут удалены. Затем нажмите кла­вишу со стрелкой вверх, чтобы восстановить команду из истории, и замените ls на rm.

ln — создание ссылок

Команда ln применяется для создания жесткой или символической ссылки Ее можно использовать одним из двух способов: создает символическую ссылку, где элементом может быть файл или каталог. Жесткие ссылки — это первоначальный способ создания ссылок в Unix; символи­ческие ссылки — более позднее изобретение. По умолчанию каждый файл имеет одну жесткую ссылку, определяющую его имя. Создавая жесткую ссылку, мы соз­даем дополнительную запись в каталоге для файла Жесткие ссылки имеют два важных ограничения О Жесткая ссылка не может указывать на файл за пределами собственной файловой системы Это означает, что ссылка не может указывать на файл, находя­щийся в другом разделе диска О Жесткая ссылка не может указывать на каталог. Жесткая ссылка неотличима от самого файла В отличие от символических ссы­лок, при выводе списка с содержимым каталогов жесткие ссылки никак не вы­деляются При удалении жесткой ссылки удаляется только сама ссылка, а файл остается на месте (то есть пространство, занимаемое файлом, не освобождается), пока не будут удалены все жесткие ссылки на файл Знать о существовании жестких ссылок важно, потому что они будут встречаться вам время от времени, но в современной практике предпочтение отдается симво­лическим ссылкам, о которых рассказывается далее Символические ссылки были придуманы с целью преодолеть ограничения жест­ких ссылок Когда создается символическая ссылка, в действительности создается файл особого типа, содержащий текстовый указатель на файл или каталог В не­котором отношении они действуют подобно ярлыкам в Windows, но, конечно же, появились задолго до ярлыков Windows ;-) Файл, на который указывает символическая ссылка, и сама символическая ссыл­ка почти неотличимы друг от друга. Например, если попытаться что-то записать в символическую ссылку, запись будет выполнена в файл, на который она указы­вает. Однако при удалении символической ссылки удаляется только символиче­ская ссылка, но не файл Если удалить файл до того, как будет удалена символи­ческая ссылка, ссылка останется на месте, но будет указывать в никуда О таких ссылках говорят, что они «битые». Во многих реализациях команда ls выделяет битые ссылки цветом, например, красным, чтобы обратить на них внимание Идея ссылок может показаться странной и непонятной, но оставьте ее пока. Мы опробуем их на практике, и многое, возможно, для вас прояснится Поскольку мы собираемся на практике опробовать некоторые операции с файла­ми, давайте выделим безопасный уголок для «игр» с командами управления фай­лами. Прежде всего нам понадобится каталог, в котором мы будем практиковать­ся. Создайте такой каталог в своем домашнем каталоге и назовите его playground. Для создания каталогов используется команда mkdir. Чтобы создать каталог play­ground, проверьте сначала, находитесь ли вы в домашнем каталоге, и только потом создайте новый каталог: Чтобы немножко украсить вашу песочницу, создайте внутри playground пару каталогов с именами dir1 и dir2 Для этого смените текущий рабочий каталог на playground и выполните еще одну команду mkdir:

Обратите внимание, что команда mkdir может принимать несколько аргументов, это позволяет создать два каталога одной командой

Копирование файлов

Далее, добавим немного данных в нашу песочницу Для этого скопируем какие - нибудь файлы. Командой cp скопируйте файл passwd из каталога /etc в текущий рабочий каталог Обратите внимание на сокращение, обозначающее текущий рабочий каталог, — точ­ку в конце команды. Если после этого выполнить команду ls, мы увидим наш файл: Команда cp вновь скопировала файл, но на этот раз вывела короткое сообщение, указывающее, что операция была выполнена Обратите внимание, что cp переза­писала первую копию без каких-либо предупреждений Это как раз тот случай, когда cp полагает, что вы знаете, что делаете Чтобы вывести предупреждение, включите параметр - i: Рассуждая о жестких ссылках, полезно представлять файлы состоящими из двух частей: раздела с данными, где хранится содержимое файла, и раздела с именем, где хранится имя файла Создавая жесткую ссылку, мы фактически создаем до­полнительный раздел с именем, ссылающийся на тот же раздел с данными. Цепоч­ку дисковых блоков система присваивает тому, что называется индексным узлом (inode), который затем присваивается разделу с именем То есть каждая жесткая ссылка ссылается на определенный индексный узел с содержимым файла Команда ls может извлекать эту информацию. Для этого ее нужно вызвать с па­раметром - i: В этой версии списка в первом поле отображается номер индексного узла, и, как можно видеть, оба имени, fun и fun-hard, ссылаются на индексные узлы с одним и тем же номером, а это подтверждает, что они соответствуют одному и тому же файлу Диспетчеры файлов в GNOME и KDE предоставляют простой автоматизированный способ создания символических ссылок. Если в GNOME во время перетаскивания файла мышью удерживать нажатыми клавиши CTRL и SHIFT, вместо копирования (или перемещения) файлов будет выполнена операция создания ссылки. В KDE, когда перетаскиваемый файл сбрасывается в целевой каталог, появляется небольшое меню, предлагающее выбор из трех операций: скопировать, переместить или создать ссылку. Мы узнали много нового, но чтобы информация усвоилась, требуется время. Вы­полняйте упражнения в песочнице раз за разом, пока не почувствуете, что пони­маете их смысл На данном этапе очень важно надежно усвоить, как работают основные команды управления файлами и групповые символы. Не бойтесь выйти за рамки предложенных упражнений — добавьте дополнительные файлы и каталоги, поэкспериментируйте с групповыми символами для определения групп файлов в разных операциях. Идея ссылок на первый взгляд может показаться малопо­нятной, поэтому уделите время их исследованию Зачастую они оказываются на­стоящим спасательным кругом До настоящего момента мы видели группы мистических команд, каждая из кото­рых имеет свои таинственные параметры и аргументы. Теперь мы удалим часть этой таинственности и даже создадим несколько собственных команд. В этой гла­ве будут представлены следующие команды: Команда может быть: Выполняемой программой, как те файлы, что мы видели в каталоге /usr/bin. К этой категории относятся: скомпилированные двоичные программы, напри­мер, написанные на C и C++; программы, написанные на языках сценариев, та­ких как shell, Perl, Python, Ruby и др. Встроенной командой, реализованной внутри самой командной оболочки. Командная оболочка bash поддерживает множество внутренних команд, кото­рые так и называют — встроенными (shell builtins) . Команда cd, например, — это встроенная команда Функцией командной оболочки. Функции командной оболочки (shell functions) — это миниатюрные сценарии на языке командной оболочки, встро­енные в окружение Мы еще вернемся к вопросам настройки окружения и соз­дания функций командной оболочки в последующих главах, а пока просто помните об их существовании Псевдонимом. Псевдоним (alias) — это команда, которую мы можем опреде­лить сами, сконструировав ее из других команд

Идентификация команд

Часто бывает полезно точно знать, какому из четырех типов принадлежит коман­да, и Linux предлагает пару способов узнать это Команда type — это встроенная команда, которая сообщает тип указанной ей команды Вызывается она следующим образом: Здесь мы видим результаты определения типов трех разных команд Обратите внимание, что команда ls (в дистрибутиве Fedora) фактически является псевдо­нимом (alias) команды ls с параметром --color=tty. Теперь-то мы знаем, почему результаты команды ls отображаются в цвете! Иногда в системе имеется более одной версии исполняемой программы. Это до­вольно редкое явление для настольных систем, но вполне обычное для больших серверов Точно определить местоположение данного исполняемого файла позво­ляет команда which: which ищет только исполняемые программы, она не способна выявлять встроенные команды или псевдонимы, замещающие фактические исполняемые программы Если попытаться с помощью which определить местоположение встроенной коман­ды (например, cd), мы либо ничего не получим, либо получим сообщение об ошибке: Это своеобразное сообщение «command not found» (команда не найдена) . Теперь, зная тип команды, можно поискать документацию с описанием, доступ­ную для каждого вида команд. bash имеет встроенную справку для каждой встроенной команды Чтобы полу­чить ее, введите help с именем встроенной команды Например: Примечание к форме записи: квадратные скобки в описании синтаксиса команды указывают на необязательность элемента Вертикальная черта используется для перечисления взаимоисключающих вариантов В примере с описанием коман­ды cd, приведенном выше, ее синтаксис описывается как cd [-L|-P] [dir]. Эта форма записи говорит, что команда cd может принимать необязательный па­раметр - L или - P и необязательный аргумент dir Несмотря на то что help дает краткое и точное описание команды cd, это описание не может служить инструкцией по использованию, и, как вы можете видеть, в нем упоминается многое из того, чего мы еще не знаем! Но не волнуйтесь, со всем этим мы познакомимся в свое время Многие выполняемые программы поддерживают параметр --help для вывода описания синтаксиса и параметров, поддерживаемых командой Например: Создает КАТАЛОГ(и), если он еще не существует. Некоторые программы не поддерживают параметр --help, но вы все равно про­буйте передать его Часто в результате выводится сообщение об ошибке, содержа­щее ту же информацию о порядке использования Большинство программ, предназначенных для использования в командной стро­ке, предоставляют официальную документацию, которую называют страницей справочного руководства (man-страницу) . Для просмотра этих страниц использу­ется специальная программа постраничного просмотра man, например: man программа Страницы справочного руководства могут несколько отличаться друг от друга оформлением, но в общем случае содержат заголовок, краткий обзор синтаксиса команды, описание назначения команды и список всех параметров с их описани­ем Однако страницы справочного руководства обычно не включают примеры ис­пользования, и их главная цель — служить справочником, а не инструкцией по использованию Для примера попробуйте вывести страницу справочного руко­водства для команды ls: В большинстве систем Linux man использует less для вывода страницы, поэтому при просмотре страницы можно использовать все известные команды less «Руководство», которое отображает man, разбито на разделы и охватывает не толь­ко пользовательские команды, но и команды системного администрирования, программные интерфейсы, форматы файлов и многое другое перечис­лены разделы справочного руководства Иногда, чтобы найти искомое, нужно заглянуть в конкретный раздел. Это актуаль­но для форматов файлов, названия которых часто совпадают с именами команд Если номер раздела не указан, man всегда будет возвращать первую найденную страницу, обычно из раздела 1 Ниже приведен пример прямого указания номера раздела: Кроме того, существует возможность найти страницы справочного руководства для близких совпадений с искомым термином Несмотря на неточность, этот под­ход иногда оказывается полезным Ниже приводится пример поиска страниц справочного руководства по слову floppy:

Страницы справочного руководства входящие в состав Linux

Как вы могли убедиться, страницы справочного руководства, входящие в состав Linux и других Unix-подобных систем, играют роль справочной документации, но не инструк­ций по использованию. Многие страницы очень сложно читать, но, как мне кажется, первый приз за сложность можно было бы присудить странице с описанием bash. Ра­ботая над книгой, я очень внимательно прочитал эту страницу, чтобы убедиться, что не упустил ни одной важной темы. Когда я ее распечатал, у меня получилось больше 80 страниц чрезвычайно плотного текста, структура которого не имеет никакого смысла для начинающих пользователей. С другой стороны, эта страница очень точная и краткая и содержит полную информацию. Поэтому почитайте ее, если у вас есть запас терпения, а затем ждите того момента, когда вы сможете читать ее и прочитанное будет наполнено для вас смыслом. Программа whatis выводит имя и однострочное описание из страницы справочно­го руководства, соответствующей искомому слову: В проекте GNU имеется альтернативное руководство Info, которое часто назы­вают info-страницами. Info-страницы выводятся с помощью программы чтения с подходящим названием Программа info читает info-файлы, организованные в древовидную структуру, каждый из которых содержит отдельную тему. Info-файлы включают гиперссыл­ки, с помощью которых можно перемещаться от узла к узлу. Гиперссылку можно узнать по начальному символу звездочки Гиперссылки активируются при уста­новке текстового курсора на них и осуществляют переход при нажатии клавиши ENTER. Чтобы вывести info-страницу, введите команду info и добавьте после нее необяза­тельное имя интересующей программы перечислены команды, которые можно использовать для управления программой во время чтения info-страницы. Большинство программ из числа обсуждавшихся до сих пор, является частью пакета coreutils проекта GNU, поэтому о них можно получить дополнительную информацию командой Она выведет страницу с меню, состоящим из гиперссылок на документацию для каждой программы, входящей в состав пакета coreutilsМногие программные пакеты, установленные в вашей системе, включают файлы с документацией, размещаемые в каталоге /usr/share/doc. Большинство из них имеют простой текстовый формат и могут просматриваться с помощью less Не­которые файлы имеют формат HTML и могут просматриваться с помощью веб­браузера. Можно также встретить файлы с расширением. gz. Это сжатые файлы, обработанные программой-архиватором gzip. Пакет gzip включает специальную версию less с именем zless, которая выводит содержимое текстовых файлов, сжа­тых архиватором gzip А теперь проведем первый опыт по программированию! У нас есть возможность создавать собственные команды с помощью команды alias. Но прежде чем на­чать, познакомимся с одной маленькой хитростью командной строки Она позво­ляет уместить в одной строке несколько команд, для чего нужно просто отделить их друг от друга точкой с запятой: Как видите, мы поместили три команды в одну строку. Первая выполняет пере­ход в каталог /usr, вторая выводит его содержимое, и третья осуществляет воз­врат в предыдущий каталог (команда cd -), поэтому по завершении мы ока­зываемся там же, где и были. Давайте теперь с помощью alias превратим эту последовательность в новую команду. Первое, что мы должны сделать, — при­думать имя для новой команды Пусть это будет test Но прежде чем продол­жить, хорошо бы проверить, не занято ли уже имя test. Для этого воспользуемся командой type: Обратите внимание на структуру этой команды: alias имя='строка' За командой alias следует имя, сразу за которым (то есть без пробелов) следует знак «равно» и строка в кавычках, описывающая действие, присваиваемое имени. После определения псевдонима его можно подставлять везде вместо команды А удаляется псевдоним с помощью команды unalias: Несмотря на то что в этом примере мы постарались не использовать имя суще­ствующей команды, иногда это бывает полезно Часто это делается, чтобы при­менить наиболее желательные параметры к каждому вызову команды В примере, приведенном выше, мы видели, что команда ls часто определяется как псевдо­ним, — это позволяет реализовать вывод информации в цвете: Теперь, когда мы узнали, как найти документацию с описанием команд, поупраж­няйтесь самостоятельно и найдите описание всех команд, встретившихся вам в этой книге. Познакомьтесь с их дополнительными параметрами и опробуйте их!

Стандартный ввод, вывод и вывод ошибок

Многие программы, которыми мы уже пользовались, что-нибудь выводят на кон­соль Этот вывод часто делится на два типа Первый — результаты работы про­граммы, то есть данные, для получения которых создавалась программа Вто­рой — сообщения о состоянии или об ошибках, извещающие нас о самочувствии программы. Для демонстрации этого механизма нам понадобится несколько команд Мы уже упоминали команду, которая может получать данные со стандартного ввода Это команда less. Теперь используем less для постраничного отображения вывода любой команды, которая посылает свои результаты в стандартный вывод: Это очень удобно! С помощью этого приема можно со всем комфортом исследо­вать вывод любой команды, посылающей результаты на стандартный вывод Конвейеры часто используются для выполнения сложных операций с данными Они позволяют объединить вместе несколько команд Часто команды, используе­мые таким способом, называют фильтрами. Фильтры принимают ввод, изменяют его определенным образом и выводят результат Первый из таких фильтров, ко­торый мы опробуем, — команда sort. Представьте, что нам необходимо составить список всех выполняемых программ в каталогах /bin и /usr/bin, расположив их по алфавиту, и затем вывести его: Поскольку в команде указаны два каталога (/bin и /usr/bin), вывод команды ls будет состоять из двух сортированных списков, по одному для каждого каталога Добавив команду sort в конвейер, мы изменили данные, чтобы получить единый сортированный список Команда uniq часто используется в комбинации с командой sort. uniq принимает сортированный список данных либо со стандартного ввода, либо из файла, имя которого можно передать в единственном аргументе (за подробностями обращай­тесь к странице справочного руководства (man) для команды uniq), и по умолча­нию удаляет повторяющиеся строки из списка Поэтому, чтобы гарантировать отсутствие дубликатов в нашем списке (то есть любых программ с одинаковыми именами в каталогах /bin и /usr/bin), добавим uniq в конвейер: Когда меня просят объяснить разницу между Windows и Linux, я часто привожу ана­логию с игрушками. Windows — это как игровая приставка Game Boy. Вы идете в магазин и покупаете новенькую сияющую приставку с игрой в комплекте. Приносите ее домой, включа­ете и играете. Отличная графика, чудные звуки. Но спустя некоторое время игра надоедает. Вы опять идете в магазин и покупаете другую игру. Так повторяется снова и снова. Наконец, вы возвращаетесь в магазин и говорите человеку за при­лавком: «Я хочу игру, которая делает это!» — а в ответ слышите, что такой игры не существует, потому что на нее нет спроса. Тогда вы говорите: «Но мне нужно всего лишь изменить вот это!» А продавец за прилавком говорит, что это невозможно. Игры продаются зашитыми в картриджи. И тут вы понимаете, что ваша приставка ограничена кругом игр, при создании которых кто-то другой решил за вас, что вам нужно, а что нет. Linux, напротив, можно сравнить с самым большим в мире конструктором. Вы откры­ваете коробку и видите необозримую коллекцию деталей — огромное число железных планочек, болтиков, гаечек, шестеренок, колесиков и моторчиков и несколько рекомен­даций по сборке. Вы начинаете играть. Сначала создаете один предлагаемый образец, затем другой. Затем вы обнаруживаете, что у вас появились собственные идеи новых конструкций и механизмов. И вам не нужно возвращаться в магазин, потому что у вас уже есть все, что требуется. Конструктор формирует ваше воображение. Он позволяет создать то, что вы хотите. Выбор игрушки, конечно же, дело глубоко личное, но признайтесь честно: какая игрушка принесла бы вам большее удовлетворение? Как обычно, загляните в документацию с описанием каждой команды, пред­ставленной в этой главе. Здесь были показаны только самые общие примеры их использования, и все они имеют множество интересных параметров По мере накопления опыта работы в Linux мы увидим, что поддержка перенаправления в командной оболочке чрезвычайно полезна для решения специализированных задач. Многие команды используют стандартный ввод и стандартный вывод, и почти все программы командной строки используют стандартный вывод оши­бок для отображения информационных сообщений

Взгляд на мир глазами командной оболочки

В этой главе мы посмотрим, что происходит в командной строке после нажатия клавиши ENTER. И в процессе исследования некоторых интересных и сложных механизмов командной оболочки мы будем пользоваться только одной новой командой: Каждый раз, когда вы вводите команду и нажимаете ENTER, bash выполняет не­сколько операций с текстом, прежде чем выполнит вашу команду Мы уже виде­ли пару примеров, где простая последовательность символов, например, может много значить для командной оболочки Процесс, который происходит при этом, называется подстановкой (expansion). То есть вы вводите что-то, и это что-то за­мещается чем-то другим, прежде чем командная оболочка продолжит обработку Чтобы показать, что все это значит, возьмем для примера команду echo — встроен­ную команду, выполняющую очень простую операцию: она выводит свои тексто­вые аргументы в стандартный поток вывода Что это? Почему echo не вывела символ? Как вы помните из опытов с группо­выми символами, символ означает «последовательность любых символов в име­ни файла», правда, в том обсуждении не рассказывалось, как командная оболоч­ка делает это На самом деле все просто: перед тем, как выполнить команду echo, оболочка замещает символ чем-то другим (в данном случае именами файлов в текущем рабочем каталоге) . После нажатия клавиши ENTER командная оболочка автоматически производит подстановку любых условных символов в командной строке, прежде чем выполнить ее, поэтому команда echo не увидела — она по­лучила уже готовый результат подстановки Теперь вы понимаете, что в действи­тельности echo действует в точности с нашими ожиданиями? Механизм работы групповых символов называется подстановкой пути (pathname expansion) . Если вернуться к некоторым приемам, продемонстрированным в пре­дыдущих главах, мы увидим, что в действительности они основаны на подстанов­ке Допустим, содержимое домашнего каталога выглядит вот так: Как вы помните из вводного обсуждения команды cd, символ тильды (~) име­ет специальное значение Если он используется в начале слова, то замещается именем домашнего каталога указанного пользователя или, если пользователь не указан, именем домашнего каталога текущего пользователя: Как мы знаем, файлы с именами, начинающимися с точки, считаются скрытыми. Меха­низм подстановки пути также учитывает это. Подстановка, такая как На первый взгляд кажется, что можно было бы включить скрытые файлы в подстановку, добавив в начало шаблона символ точки, например: Да, такой подход даст желаемое. Однако, если внимательно исследовать результаты, можно заметить, что в них также присутствуют имена. (точка) и.. (две точки). Так как эти имена соответствуют текущему рабочему каталогу и родительскому каталогу, применение такого шаблона может привести к неправильным результатам. Убедимся в этом с помощью команды Чтобы обеспечить правильную подстановку пути в такой ситуации, следует использовать специализированный шаблон. Следующий шаблон действует правильно: Этот шаблон замещается именами файлов, начинающимися с точки, за которой следует хотя бы один символ, кроме точки, за которым в свою очередь может следовать любое количество других символов. Командная оболочка поддерживает также подстановку результатов арифмети­ческих выражений Это позволяет использовать командную строку как кальку­лятор: Для подстановки арифметических выражений используется следующий формат: $((выражение)) где выражение — это арифметическое выражение, состоящее из значений и ариф­метических операторов Механизм подстановки арифметических выражений позволяет использовать толь­ко целые числа (невещественные), зато поддерживает множество арифметических операций перечислены некоторые из поддерживаемых операторов. Для группировки подвыражений допускается использование одиночных круглых скобок. С помощью этого приема выражение, приведенное выше, можно перепи­сать, как показано ниже, и получить тот же результат, но при этом будет использо­ваться одна операция подстановки вместо двух: Следующий пример демонстрирует использование операторов деления и получе­ния остатка. Обратите внимание, как действует целочисленное деление:

Подстановка фигурных скобок

Самым малопонятным, пожалуй, выглядит результат подстановки фигурных скобок С помощью этого механизма из одного шаблона, содержащего фигурные скобки, создается множество текстовых строк Например: Шаблоны с фигурными скобками могут содержать начальную часть, которая называется преамбулой, и заключительную часть, которая называется эпилогом. Внутри фигурных скобок находится список строк, разделенных запятыми, или диапазон целых чисел или одиночных символов. Использование пробелов внутри фигурных скобок не допускается Ниже приводится пример с использованием диапазона целых чисел: Какую пользу можно извлечь из этого? Такая возможность может пригодиться для формирования списков файлов или каталогов, которые требуется создать Например, фотограф, имеющий огромную коллекцию фотографий и желающий организовать ее по годам и месяцам, мог бы начать с создания группы каталогов с именами, состоящими из номера года и месяца. Благодаря этому имена ката­логов будут отсортированы в хронологическом порядке Можно было бы ввести полный список каталогов, но это обременительно и чревато ошибками Вместо этого выполним следующую команду: В этой главе мы лишь кратко коснемся подстановки параметров, а детально об­судим ее позже. Эта возможность полезнее в сценариях на языке командной обо­лочки, чем непосредственно в командной строке Многие из ее возможностей имеют отношение к способности системы хранить маленькие фрагменты данных и присваивать этим фрагментам имена. Многие такие фрагменты, правильнее их называть переменными, уже существуют и доступны для исследования На­пример, переменная с именем USER хранит ваше имя пользователя. Подстановка параметра и получение содержимого переменной USER выполняется следующим образом: Возможно, вы обратили внимание, что если в других вариантах подстановки до­пустить ошибку в шаблоне, подстановка не будет выполнена и команда echo про­сто выведет ошибочный шаблон В случае с подстановкой параметров все иначе: если ошибиться в имени переменной, подстановка все равно будет выполнена, но результатом будет пустая строка: Подстановка команд позволяет использовать поток вывода команд в качестве ар­гументов других команд: Здесь результат команды which cp передается как аргумент команде ls, благодаря чему мы получаем информацию о программе cp, не зная полного пути к ней Под­становка команд не ограничивается такими простыми командами Можно исполь­зовать целые конвейеры (здесь показана только часть вывода): В этом примере результаты конвейера превратились в список аргументов коман­ды file. Механизм подстановки команд имеет альтернативный синтаксис, унаследо­ванный от более старых командных оболочек, который также поддерживается в bash В нем вместо знака доллара и круглых скобок используются обратные апострофы: Теперь, после знакомства с множеством способов подстановки, поддерживаемых командной оболочкой, можно начинать учиться управлять ими Например, взгля­ните на эту команду: В первом примере механизм разбиения на слова удалил дополнительные про­белы из списка аргументов команды echo Во втором — механизм подстановки параметров подставил пустую строку вместо $1, потому что не нашел такую пе­ременную Командная оболочка предоставляет механизм, который называется экранированием (quoting), для выборочного подавления нежелательной подста­новки

Двойные кавычки

Первый тип экранирования, который мы рассмотрим, — двойные кавычки. Если заключить текст в двойные кавычки, все специальные символы потеряют свое специальное значение и будут интерпретироваться как обычные символы Ис­ключение составляют: $ (знак доллара), \ (обратный слеш) и ' (обратный апо­строф) То есть разбиение на слова, подстановка путей, подстановка тильды и подстановка фигурных скобок выполняться не будут, но подстановка параме­тров, подстановка значений арифметических выражений и подстановка команд все еще будут выполняться Благодаря двойным кавычкам мы сможем обраба­тывать имена файлов с пробелами Представьте, что мы по ошибке создали файл с именем Два словам. Если попытаться использовать это имя в командной строке, механизм разбиения слов будет интерпретировать его как два отдельных аргумента: Добавив двойные кавычки, можно запретить разбиение слов и получить желае­мый результат; кроме того, с помощью двойных кавычек мы исправим ошибку: Вот так! Теперь не нужно вводить эти противные двойные кавычки Запомните: подстановка параметров, подстановка значений арифметических выражений и подстановка команд все еще выполняются в двойных кавычках: Давайте отвлечемся и посмотрим, какой эффект оказывают двойные кавычки на подстановку команд Сначала рассмотрим действие механизма разбиения на сло­ва В одном из примеров, приведенных выше, мы видели, как механизм разбиения на слова удаляет дополнительные пробелы из текста: [me@linuxbox ~]$ echo this is a test По умолчанию этот механизм находит пробелы, символы табуляции и символы перевода строки и интерпретирует их как разделители слов То есть вне кавычек упомянутые символы не считаются частью текста. Они являются лишь разде­лителями. Поскольку они делят слова на аргументы, получается, что в нашем примере командная строка состоит из команды и четырех аргументов Одна­ко если добавить двойные кавычки, разбиение на слова выполняться не будет и внутренние пробелы не будут считаться разделителями — они станут частью аргумента: После добавления двойных кавычек командная строка будет состоять из команды и одного аргумента Тот факт, что символы перевода строки интерпретируются механизмом разбие­ния на слова как разделители, вызывает интересный и трудноуловимый эффект при подстановке команд Взгляните: В первом случае подстановка команд без кавычек привела к созданию командной строки с 38 аргументами, а во втором случае получилась командная строка с од­ним аргументом, включающим внутренние пробелы и символы перевода строки Если вам требуется подавить все подстановки, используйте одиночные кавычки Ниже для сравнения приводятся результаты неэкранированной команды и коман­ды, экранированной двойными и одиночными кавычками: Как видите, каждый следующий уровень экранирования все больше и больше по­давляет подстановку Иногда бывает необходимо экранировать только один символ. Для этого доста­точно добавить перед символом обратный слеш, который в данном случае назы­вается экранирующим символом (escape character) . Часто этот прием используется в двойных кавычках, чтобы выборочно предотвратить подстановку Экранирование символов также широко применяется для подавления специ­ального значения символов в именах файлов Например, в именах файлов допу­скается использование символов, которые имеют специальное значение для ко­мандной оболочки. К их числу относятся: $, !, &, (пробел) и др. Чтобы включить специальный символ в имя файла, его достаточно экранировать, как показано ниже: Чтобы включить сам экранирующий символ, его также нужно экранировать, вве­дя \\ Имейте в виду, что внутри одиночных кавычек обратный слеш теряет свое специальное значение и интерпретируется как обычный символ По мере накопления опыта использования командной оболочки мы все чаще бу­дем использовать возможности подстановки и экранирования, поэтому важно хо­рошо понимать, как они работают. Фактически можно смело утверждать, что эти два механизма являются наиболее важными для изучения аспектами командной оболочки Без надлежащего понимания того, как действует подстановка, команд­ная оболочка будет оставаться источником непонимания и домыслов, при этом многие ее возможности останутся неиспользованными Обратный слеш используется не только в роли экранирующего символа, но и как часть специальных символов, которые называют управляющими кодами (control codes). Пер­вые 32 символа в схеме кодирования ASCII использовались для передачи различных команд в устройствах, таких как телетайп. Некоторые из этих кодов хорошо знакомы вам (табуляция, забой, перевод строки и возврат каретки), тогда как другие — нет (пустой символ, конец передачи и подтверждение), как показано перечислены некоторые наиболее известные управляющие последова­тельности. Идея использования обратного слеша зародилась в языке программирования C и была заимствована многими другими языками, включая язык командной оболочки. Параметр - e команды echo включает интерпретацию управляющих последователь­ностей. Их можно также заключать в конструкцию $' '. Ниже демонстрируется ис­пользование команды sleep, простой программы, которая всего лишь ждет указанное число секунд и завершается, для создания элементарного таймера.

Продвинутые приемы работы с клавиатурой

Я часто шутливо описываю Unix как «операционную систему для тех, кто любит пе­чатать» . Казалось бы, сам факт наличия командной строки доказывает это. Но в дей­ствительности пользователи командной строки не любят печатать слишком много. Зачем, если есть так много команд с короткими именами, таких как cp, ls, mv и rm? Фактически одной из самых заветных целей командной строки является умень­шение объема ввода — возможность выполнить большую часть работы всего не­сколькими нажатиями клавиш. Другая цель — не позволить рукам оторваться от клавиатуры и коснуться мыши. В этой главе мы рассмотрим возможности bash, увеличивающие скорость и эффективность использования клавиатуры. Для поддержки операций редактирования командной строки bash использует би­блиотеку (коллекцию подпрограмм, которую могут использовать разные програм­мы) с именем Readline. Мы уже видели некоторые из них. Например, нам знакомы клавиши со стрелками влево и вправо, перемещающие курсор, но существует еще целое множество других операций. Рассматривайте их как дополнительные инстру­менты, которые можно использовать в работе Необязательно стремиться изучить их все, но многие из них весьма практичны Выбирайте те, что вам понравятсяВ документации к Readline используется термин killing and yanking (удаление и возврат), обозначающий операцию, которую обычно называют вырезанием и вставкой (cutting and pasting) перечислены комбинации клавиш, вы­полняющие вырезание и вставку Вырезанные элементы сохраняются в кольце­вом буфере, который называется kill-ring (кольцо удалений) .Отважившиеся заглянуть в документацию к Readline, которая находится в разделе «READLINE», на странице справочного руководства (man) для bash, столкнутся с тер­мином клавиша meta (meta key). На современных клавиатурах ей соответствует кла­виша ALT, но так было не всегда. В стародавние времена (до появления IBM-совместимых персональных компьютеров, но после появления Unix) персональные компьютеры не были так широко распростра­нены. Иногда их заменяли устройства, называемые терминалами. Терминал — это коммуникационное устройство с текстовым дисплеем и клавиатурой, имеющее внутри столько электроники, сколько необходимо для отображения символов и перемеще­ния курсора. Терминалы подключались (обычно посредством последовательного кабеля) к большому компьютеру или коммуникационной сети большого компьютера. В то время существовало очень много различных терминалов, имевших разные кла­виатуры и дисплеи с разными функциональными возможностями. Так как все они поддерживали как минимум набор символов ASCII, разработчикам программного обеспечения, пишущим переносимые приложения, необходимо было прийти к общему знаменателю. В системах Unix применяется очень сложный способ использования терминалов и их разнообразных возможностей. Поскольку разработчики Readline не были уверены в наличии специализированной управляющей клавиши, они изо­брели ее и назвали meta. На современных клавиатурах роль клавиши meta играет ALT, однако если вы все еще используете терминал (до сих пор поддерживаются в Linux!), можно просто нажать и отпустить клавишу ESC, и вы получите эффект нажатия и удержания клавиши ALT.

Механизм дополнения

Другой вариант помощи пользователям реализован в командной оболочке в виде механизма дополнения (completion) . Дополнение происходит, когда в процессе ввода команды нажимается клавиша TAB. Давайте посмотрим, как это работает. Допустим, что ваш домашний каталог содержит следующее: Обратили ли вы внимание, как командная оболочка дополнила командную строку за вас? Попробуйте теперь набрать следующую строку — и снова не нажимайте ENTER): Дополнения не произошло — просто прозвучал звуковой сигнал Так получилось потому, что символу D соответствует более одного элемента в каталоге. Чтобы ко­мандная оболочка дополнила вашу строку, предложенная вами «подсказка» долж­на иметь однозначное продолжение Попробуйте продолжить ввод: Этот пример демонстрирует дополнение путей как наиболее частый случай ис­пользования дополнения Однако дополнение также работает с именами перемен­ных (когда слово начинается с символа $), именами пользователей (когда слово начинается с символа ~), командами (когда дополняемое слово является первым в командной строке) и сетевыми именами компьютеров (когда слово начинается с символа @) Дополнение сетевых имен компьютеров действует только в отноше­нии имен, перечисленных в /etc/hosts. С механизмом дополнения связано несколько управляющих комбинаций клавиш Существует еще несколько команд, смысл которых для меня не совсем ясен. Пол­ный список вы сможете найти на странице справочного руководства (man) для bash, в разделе «READLINE». Последние версии bash реализуют механизм программируемого дополнения. Программируемое дополнение дает возможность добавлять дополнительные правила. Обычно это делается с целью добавить поддержку определенных приложений. Например, можно добавить дополнение списка параметров команды или файлов определенного типа, поддерживаемых приложением. В Ubuntu определено огромное множество таких правил. Программируемое дополнение реализуется посредством функций командной оболочки — небольших мини-сценариев, о которых будет рассказываться в следующих главах. Если вам любопытно, попробуйте выполнить команду set | less и вы увидите их. Однако не все дистрибутивы включают эти функции по умолчанию. Как рассказывалось в главе 1, bash поддерживает историю вводившихся команд. Этот список команд хранится в домашнем каталоге, в файле с именем. bash_history. Механизм истории помогает уменьшить объем ручного ввода, особенно в сочета­нии с командами редактирования командной строки Просмотреть содержимое истории можно в любой момент с помощью команды: [me@linuxbox По умолчанию bash хранит последние 500 введенных команд. Как изменить это значение, мы узнаем в главе 11. А теперь представим, что вам понадобилось най­ти команды, использовавшиеся для получения списка содержимого /usr/bin. Вот один из возможных Здесь число 88 — это порядковый номер записи команды в списке истории. Зная это число, можно воспользоваться еще одной разновидностью подстановки, которая называется подстановкой записей истории (history expansion). Для этого введите: и bash заменит!88 содержимым 88-й записи в списке истории. Подробнее об этой форме подстановки записей истории мы поговорим чуть ниже bash также дает возможность выполнять поступательный поиск в списке истории. Это означает, что bash может выполнять поиск в списке истории по мере ввода символов, уточняя результаты с вводом каждого нового символа Чтобы запу­стить поступательный поиск, нажмите комбинацию CTRL+R и введите искомый текст. Закончив поиск, нажмите ENTER, чтобы выполнить команду, или CTRL+J, что­бы скопировать запись из списка истории в текущую командную строку Чтобы найти следующее вхождение текста (переместиться «вверх» по списку истории), нажмите CTRL+R еще раз. Чтобы завершить поиск, нажмите CTRL+G или CTRL+C. Следующий пример демонстрирует, как действует поиск: Приглашение к вводу изменится, показывая, что выполняется поступательный поиск в обратном порядке Под словами «в обратном порядке» подразумевается, что поиск выполняется от «текущего момента» до некоторого момента в прошлом Далее мы начинаем ввод искомого текста, в данном примере /usr/bin: Механизм поиска сразу же возвращает результат Теперь, чтобы выполнить най­денную команду, необходимо нажать ENTER, или вы можете скопировать команду в командную строку для дальнейшего редактирования, нажав CTRL+J. Давайте ско­пируем ее. Нажмите CTRL+J:

Операционные системы следующие традициям Unix

Операционные системы, следующие традициям Unix, отличаются от систем, сле­дующих традициям MS-DOS, тем, что являются не только многозадачными, но и многопользовательскими. Что это означает на самом деле? Это означает, что компьютером могут одновре­менно пользоваться несколько человек Несмотря на то что обычно компьютер имеет всего одну клавиатуру и монитор, это обстоятельство не мешает совмест­ному пользованию Например, если компьютер подключен к локальной сети или к Интернету, удаленные пользователи смогут зайти на него через ssh (secure shell — безопасная командная оболочка) и выполнять операции. Фактически уда­ленные пользователи могут запускать приложения с графическим интерфейсом и получать изображение на удаленном дисплее. X Window System поддерживает такую возможность изначально Командная оболочка поддерживает специализированный вид подстановки — под­становку записей из списка истории при использовании символа! Мы уже видели, как восклицательный знак, сопровождаемый числом, замещается записью из спи­ска истории. Этот вид подстановки имеет несколько разновидностей Не используйте формы! строка и!?строка, если только вы абсолютно точно не знаете содержимого записей в списке истории Механизм подстановки записей истории поддерживает также другие комбинации, но эта тема становится слишком запутанной, и мы не станем перегружать себя SCRIPT В дополнение к истории команд в bash большинство дистрибутивов Linux включают программу script, которую можно использовать для записи в файлы целых сеансов работы с командной оболочкой. Базовый синтаксис команды: script [файл] где файл — это имя файла для записи. Если файл не будет указан, сохранение сеанса будет произведено в файл typescript. Полное описание параметров и возможностей программы можно найти на странице справочного руководства (man) для script. В этой главе мы рассмотрели несколько приемов работы с клавиатурой, поддерживаемых командной оболочкой, с целью помочь истинным фанатам клавиатуры уменьшить объем работы. Я думаю, что потом, когда вы сроднитесь с командной строкой, вы сможете обратиться к этой главе, чтобы вспомнить описанные здесь приемы А пока будем считать их необязательными, но потенциально полезными Поддержка многопользовательского режима работы — не недавнее «изобретение» Linux, а возможность, глубоко внедренная в архитектуру операционной системы Учитывая окружение, в котором создавалась система Unix, это имело определен­ный смысл В те времена, когда компьютеры еще не были «персональными», они были большими и дорогими Типичная компьютерная система университета, на­пример, состояла из большого центрального компьютера в одном здании и терми­налов, разбросанных по всему университетскому городку и соединенных с боль­шим центральным компьютером Компьютер мог одновременно обслуживать множество пользователей Чтобы подобная возможность имела практическую ценность, необходим способ определенной «изоляции» пользователей друг от друга В конце концов, действия рядового пользователя не должны приводить к аварийному завершению работы компьютера, и ни один пользователь не должен иметь возможность вносить из­менения в файлы, принадлежащие другому пользователю В данной главе мы рассмотрим эту важную сторону безопасности системы и по­знакомимся со следующими командами:

Владельцы, члены группы и все остальные

Знакомясь с системой уже сталкивались со следующей проблемой при исследовании файлов, таких как /etc/shadow: Причина этого сообщения об ошибке заключается в том, что обычные пользовате­ли не имеют права читать этот файл В модели безопасности Unix пользователь может владеть файлами и каталогами Если пользователь владеет файлом или каталогом, он может управлять доступом к нему. Пользователи могут также принадлежать группе, состоящей из одного или нескольких пользователей, и получить права доступа к файлам и каталогам для членов группы, которые определяются владельцами Кроме прав доступа для группы, владелец может также определить некоторые права доступа для всех остальных, их в терминологии Unix называют мир (world) Получить информа­цию о своей идентичности можно с помощью команды id: Давайте рассмотрим этот вывод Когда создается учетная запись пользовате­ля, ей присваивается число, которое называют идентификатором пользователя (user ID), или uid Это число, исключительно ради удобства человека, отобража­ется как имя пользователя. Пользователю назначается идентификатор основной группы (primary group ID), или gid, и дополнительно пользователь может вклю­чаться в состав других групп. Предыдущий пример взят из системы Fedora. В дру­гих системах, таких как Ubuntu, вывод команды может немного отличаться. Восьмеричная (по основанию 8) и родственная ей шестнадцатеричная (по основа­нию 16) системы счисления часто используются для представления чисел в компьюте­рах. Мы, люди, рождаемся с десятью пальцами на руках (по крайней мере большинство из нас), поэтому для счета используем систему счисления с основанием 10. Компьютеры, напротив, рождаются с одним пальцем и потому используют для вычисления двоич­ную систему счисления (по основанию 2). Их числа состоят всего из двух цифр, нуля и единицы. Поэтому в двоичной системе счет выглядит В двоичной системе счисления еще можно увидеть смысл (поскольку компьютеры имеют лишь один палец), но в чем польза восьмеричной и шестнадцатеричной систем счисления? Они были придуманы для удобства человека. Очень часто небольшие пор­ции данных представляются в компьютерах битовыми шаблонами. Примером может служить представление цвета в формате RGB. В большинстве дисплеев компьютеров цвет каждого пикселя определяется тремя цветовыми составляющими: 8 бит для крас­ного цвета, 8 бит для зеленого и 8 бит для синего. Красивый сине-голубой цвет можно представить в виде 24-разрядного числа: Хотели бы вы видеть и читать такие числа весь день? Я так не думаю. Именно в таких случаях на выручку приходят другие системы счисления. Каждая цифра в шестнадца­теричной системе счисления представляет четыре двоичные цифры. В восьмеричной системе каждой цифре соответствуют три двоичные цифры. То есть 24-разрядное значение сине-голубого цвета можно сжать до 6-значного шестнадцатеричного числа: 436FCD. Поскольку цифры в шестнадцатеричных числах «выстраиваются в ряд» с би­тами в двоичных числах, можно заметить, что красный компонент нашего цвета имеет значение 43, зеленый — 6F и синий — CD. В наше время шестнадцатеричная форма записи получила большее распространение, чем восьмеричная, но, как будет показано ниже, восьмеричная форма записи все еще оказывается весьма полезной для представления групп из трех двоичных битов. При использовании восьмеричной формы записи шаблон желаемых привиле­гий определяется восьмеричными числами. Так как каждая цифра в восьмерич­ном числе определяется тремя двоичными разрядами, она точно отображается в схему хранения режима доступа к файлу.

Режимы доступа к файлу в двоичном и восьмеричном представлениях

C помощью трех восьмеричных цифр мы можем определить режим доступа к фай­лу для владельца, для группы и для остального мира. Передав аргумент 600, мы установили права для владельца, позволяющие ему читать данные из файла и записывать их в файл, и при этом отобрали все права у группы и остального мира. Несмотря на кажущееся неудобство необходимости запоминания соответствий между восьмеричными и двоичными представления­ми, вам, скорее всего, придется использовать лишь несколько наиболее популяр­ных шаблонов: 7 (rwx), 6 (rw-), 5 Команда chmod поддерживает также символическую форму определения режимов доступа к файлу Символическая форма записи делится на три части: для кого устанавливаются разрешения, какие операции с разрешениями будут выполнять­ся и на какие разрешения эти операции будут влиять. Чтобы указать, для кого устанавливаются разрешения, используется комбинация символов u, g, o и a, как показано Если не указан ни один символ, предполагается a (all — все) . Операцией может быть знак +, соответствующий добавлению заданных разрешений, знак - , соответ­ствующий отъему заданных разрешений, или знак =, указывающий, что только за­данные разрешения должны быть установлены, а все остальные отобраны Разрешения определяются символами r, w и x перечислены некоторые примеры символической формы записи Кто-то предпочитает пользоваться восьмеричной формой записи, кому-то больше нравится символическая Символическая форма записи удобна тем, что позволя­ет установить единственный атрибут, не влияя на остальные Дополнительную информацию и полный список параметров команды chmod мож­но найти на странице справочного руководства (man) . А теперь несколько слов о параметре - - recursive: он воздействует и на файлы, и на каталоги, поэтому он не так полезен, как можно было бы предположить, потому что редко требуется устанавливать одинаковые разрешения для файлов и каталогов Теперь, ознакомившись с тем, как устанавливаются разрешения для файлов и ка­талогов, вы лучше поймете диалоги установки разрешений в графическом интер­фейсе. В Nautilus (GNOME) и Konqueror (KDE) можно щелкнуть правой кноп­кой мыши на файле или на каталоге и вывести диалог со свойствами изображен такой диалог из KDE 3 . 5. Здесь вы видите, какие разрешения установлены для владельца, группы и осталь­ного мира. Если в KDE щелкнуть на кнопке Advanced Permissions (Дополнительные разрешения), появится другой диалог, в котором можно будет установить атри­буты режима по отдельности Еще один маленький шаг человека в большом мире под названием Командная строка! Команда umask определяет разрешения по умолчанию, которые устанавливаются для файла при его создании В ней с помощью восьмеричной формы записи опре­деляется битовая маска для сбрасываемых атрибутов режима доступа Сначала мы удалили существующий файл foo. txt, чтобы, так сказать, начать с чи­стого листа. Далее мы выполнили команду umask без аргумента, чтобы увидеть текущее значение маски. Она вернула нам значение 0002 (часто также использует­ся значение 0022) — восьмеричное представление действующей маски. Затем мы создали новый файл foo. txt и вывели для него разрешения. Как видите, владелец и группа получили права на чтение и запись, тогда как все остальные — только право на чтение. Весь мир не получил права на запись из-за значения маски. Давайте повторим пример, но на этот раз определим свою маску: После установки маски в значение 0000 (таким способом мы фактически выклю­чили ее) вновь созданный файл получил разрешение на запись для всего мира Чтобы лучше понять суть происходящего, мы снова должны вернуться к восьмеричным числам. Если развернуть маску в двоичное представление и сравнить ее с двоичным представлением атрибутов, можно понять, что произошло: Забудем пока про начальные нули (мы вернемся к ним чуть позже) и обратим внимание, что атрибут, соответствующий той позиции, где в маске стоит 1, был сброшен, — в данном случае право на запись для всего мира. Теперь понятно, что делает маска В любой позиции, где в маске появляется 1, соответствующий атри­бут сбрасывается Если посмотреть на значение маски 0022, легко увидеть, что оно делает: И снова атрибуты, соответствующие позициям, где в маске стоит 1, были сбро­шены. Поэкспериментируйте с другими значениями (попробуйте несколько 7), чтобы лучше усвоить, как действует маска Закончив эксперименты, не забудьте все вернуть в исходное состояние.

Разрешения в восьмеричном представлении

Обычно разрешения в восьмеричном представлении мы видим как трехзначные чис­ла, но технически более правильно выражать их четырехзначными числами. Почему? Потому что в дополнение к разрешениям на чтение, запись и выполнение существует еще несколько редко используемых разрешений. Первый атрибут — бит setuid (восьмеричное значение 4000). Если это разрешение применя­ется к выполняемому файлу, в качестве эффективного идентификатора пользователя для процесса устанавливается не идентификатор реального пользователя (пользователя, фактически запустившего программу), а идентификатор владельца программы. Чаще этот бит устанавливается для программ, владельцем которых является суперпользователь. Когда обычный пользователь запускает программу с установленным битом setuid и при­надлежащую пользователю root, программа выполняется с эффективными привилегиями суперпользователя. Это дает возможность программам обращаться к файлам и каталогам, недоступным для обычного пользователя. Очевидно, что из-за возникающих проблем безопасности число таких программ в системе должно быть сведено к минимуму. Второй редко используемый атрибут — бит setgid (восьмеричное значение 2000). По аналогии с битом setuid он устанавливает эффективный идентификатор группы для процесса, выбирая вместо идентификатора группы реального пользователя группу владельца файла. Если установить бит setgid для каталога, вновь создаваемые файлы в этом каталоге будут принадлежать группе владельца каталога, а не группе владельца файла, создавшего его. Это разрешение может пригодиться для установки на каталоги, содержимое которых должно быть доступно всем членам основной группы владельца каталога, независимо от принадлежности к основной группе владельца файла. Третий атрибут называется битом sticky (восьмеричное значение 1000). Это пережиток, оставшийся от первых версий Unix, которые предоставляли возможность пометить вы­полняемый файл как «невытесняемый». Linux игнорирует бит sticky у файлов, но если установить его для каталога, он не позволит пользователю удалять или переименовы­вать файлы, если только пользователь не является владельцем каталога, владельцем файла или суперпользователем. Это разрешение часто применяется для управления доступом к общим каталогам, таким как /tmp. Ниже приводится несколько примеров использования chmod с символической формой определения этих специальных разрешений. Первый пример — установка бита setuid на файл программы: chmod u+s program Далее — установка бита setgid на каталог: chmod g+s dir Наконец, установка бита sticky на каталог: chmod +t dir Специальные разрешения мы видим в выводе команды ls. Ниже приводится несколько примеров. Первый — программа с битом setuid: Вам редко придется изменять маску, потому что значение по умолчанию, уста­навливаемое дистрибутивом, прекрасно подходит для большинства нужд Однако в некоторых ситуациях, требующих повышенной безопасности, маску может по­надобиться изменить Время от времени возникает необходимость приобрести идентичность другого пользователя Чаще всего требуется получить привилегии суперпользователя, чтобы выполнить некоторые административные задачи, но точно так же можно «превратить» в другого обычного пользователя, чтобы, к примеру, проверить на­стройки учетной записи Существует три способа приобрести альтернативную идентичность: Мы пропустим первый способ, потому что уже знаем, как им воспользоваться, и он не так удобен, как два других В рамках сеанса работы с командной оболоч­кой команда su позволяет приобрести идентичность другого пользователя и либо начать новый сеанс командной оболочки с идентификатором этого пользовате­ля, либо запустить одиночную команду от его имени. Команда sudo позволяет администратору записать настройки в конфигурационный файл с именем /etc/ sudoers и определить конкретные команды, которые сможет выполнять тот или иной пользователь под приобретенной идентичностью

Выбор между su и sudo

Выбор между su и sudo в значительной степени определяется используемым дистрибутивом Linux. Боль­шинство дистрибутивов включают обе команды, но в настройках предпочтение отдается той или иной. Начнем с команды su. su — запуск командной оболочки с подстановкой идентификаторов пользователя и группы Команда su используется для запуска нового сеанса работы с командной оболоч­кой от имени другого пользователя Команда имеет следующий синтаксис: Если указан параметр - l, запущенная командная оболочка станет оболочкой вхо­да для указанного пользователя. Это означает, что будет загружено окружение пользователя и текущим рабочим каталогом станет домашний каталог пользова­теля Часто это именно то, что требуется Если пользователь не указан, подразу­мевается суперпользователь Обратите внимание, что (довольно необычно) пара­метр - l можно сократить до -, и эта особенность часто используется на практике Запустить командную оболочку от имени суперпользователя можно следующим образом:

После ввода команды будет запрошен пароль суперпользователя После ввода правильного пароля появится новое приглашение к вводу, показывающее, что данная командная оболочка обладает привилегиями суперпользователя (символ # в конце вместо символа $) и текущим рабочим каталогом теперь стал домашний каталог суперпользователя (обычно /root) После запуска новой оболочки можно выполнять команды с привилегиями суперпользователя Завершим работу, введя команду exit, чтобы вернуться в предыдущую командную оболочку: С помощью su можно так же просто выполнить единственную команду, не запу­ская новый интерактивный сеанс: При использовании этой формы команде su передается единственная командная строка для выполнения Не забудьте заключить команду в кавычки, чтобы предот­вратить дополнительную ее интерпретацию механизмами подстановки текущей командной оболочки: Команда sudo во многом подобна команде su, но имеет некоторые важные допол­нительные особенности. Администратор может определить порядок использова­ния sudo обычными пользователями, ограничив возможность запуска команд от имени другого пользователя (обычно суперпользователя) В частности, пользователю может быть разрешен доступ к одним командам и запрещен к другим Еще одно важное отличие состоит в том, что sudo не требует ввода пароля суперполь­зователя Для аутентификации в команде sudo пользователь должен ввести свой пароль. Например, допустим, что настройки sudo позволяют выполнить некото­рую мифическую программу резервного копирования с именем backup_script, требующую привилегий суперпользователя После ввода команды вам будет предложено ввести пароль (ваш, а не суперполь­зователя), и по завершении аутентификации указанная команда будет выполнена. Одно важное отличие между su и sudo — последняя не запускает новую команд­ную оболочку и не загружает окружение другого пользователя Это означает, что команды не требуется экранировать как-то иначе, чем при запуске той же коман­ды без использования sudo. Имейте в виду, что такое ее поведение можно пере­определить с помощью различных параметров Подробности ищите на странице справочного руководства (man) для sudo. Обычные пользователи иногда сталкиваются с необходимостью выполнить некоторую операцию, требующую привилегий суперпользователя. К числу таких операций от­носится установка и обновление программного обеспечения, правка системных кон­фигурационных файлов и доступ к устройствам. В мире Windows эта проблема часто решается передачей пользователям административных привилегий, что позволяет им решать подобные задачи. Однако программы, запускаемые такими пользователями, получают те же привилегии. В большинстве случаев это именно то, что нужно, но это также дает возможность беспрепятственной работы вредоносному программному обе­спечению, такому как вирусы.

Грань между обычными пользователями и администратора­ми

В мире Unix, вследствие многопользовательской природы этой операционной системы, всегда проводилась четкая грань между обычными пользователями и администратора­ми. Идеология Unix заключается в том, чтобы предоставлять привилегии суперполь­зователя, только когда они действительно необходимы. Для этого часто используются команды su и sudo. Еще несколько лет тому назад большинство дистрибутивов Linux использовали с этой целью команду su. Команда su не требует настройки, как команда sudo, а наличие учетной записи root — давняя традиция в Unix. Вместе это порождает проблему. Пользователи могут испытывать соблазн действовать от имени root без всякой необ­ходимости. Фактически некоторые пользователи вообще работают в своих системах, регистрируясь исключительно как root, чтобы избежать появления раздражающих со­общений «permission denied» (доступ запрещен). Такой подход ухудшает защищенность Linux, низводя ее до уровня Windows. Не самое лучшее решение. Создатели Ubuntu предприняли иной подход. По умолчанию Ubuntu запрещает регистрироваться в системе с учетной записью root (не позволяя устанавливать пароль для этой учетной записи), а для получения привилегий суперпользователя предлагает использовать sudo. Начальная учетная запись пользователя обладает полным доступом к привилегиям суперпользователя через sudo и может наделять аналогичными при­вилегиями другие, вновь создаваемые учетные записи. Чтобы увидеть, какие привилегии дает команда sudo, вызовите ее с параметром - l: [me@linuxbox ~]$ sudo –l Теперь, когда мы разобрались, как действует механизм привилегий, самое время научиться пользоваться ими. Далее демонстрируется решение типичной задачи — настройка общего каталога. Представьте себе двух пользователей, bill и karen. Оба имеют коллекции музыкальных произведений и хотели бы настроить общий ка­талог, где могли бы хранить файлы в формате Ogg Vorbis или MP3 . Пользователь bill имеет доступ к привилегиям суперпользователя через sudo. Первое, что нужно сделать, — это создать группу, куда будут входить оба пользо­вателя, bill и karen. С помощью графического инструмента GNOME для управле­ния пользователями bill создает группу с именем music и добавляет в нее пользо­вателей bill и karen, как показано на рис Как видите, каталогом владеет root, который имеет права доступа 755. Чтобы сде­лать каталог общим, bill должен изменить группу каталога и права доступа для группы: И что все это означает? А означает это следующее: владельцем каталога /usr/ local/share/Music является root, и члены группы music получают права на запись и чтение в этом каталоге. Группа music включает пользователей bill и karen; то есть bill и karen могут создавать файлы в каталоге /usr/local/share/Music. Другие поль­зователи могут просматривать содержимое каталога, но не могут создавать файлы в нем. Но остается нерешенной еще одна проблема С текущими разрешениями файлы и каталоги внутри каталога Music будут создаваться с обычными разрешениями для пользователей bill и karen: В действительности здесь наблюдаются две проблемы. Во-первых, маска umask в этой системе имеет значение 0022, что не позволяет членам группы записывать в файлы, принадлежащие другим членам группы Это не проблема, если общий каталог хранит только файлы, но так как в данном каталоге предполагается хра­нить музыкальные произведения, а музыкальные произведения обычно принято организовывать в иерархии по исполнителям и альбомам, членам группы может понадобиться создавать файлы в каталогах, принадлежащих другим членам Нам нужно изменить маску umask для пользователей bill и karen на 0002. Во-вторых, каждый файл и каталог, созданный одним членом группы, будет при­надлежать основной группе пользователя, а не группе music Исправить этот недо­статок можно установкой бита setgid на каталог: Теперь можно проверить, устранили ли проблему вновь добавленные разрешения bill устанавливает маску umask в значение 0002, удаляет предыдущий провероч­ный файл и создает новый проверочный файл и каталог: И файл и каталог теперь созданы с правильными правами доступа, позволяющи­ми всем членам группы music создавать файлы и каталоги внутри каталога Music Осталась только проблема с маской umask Дело в том, что установленная маска действует лишь до конца сеанса и сбрасывается по его завершении

Изменение своего пароля

Последняя тема этой главы: изменение собственного пароля (и паролей других пользователей при наличии привилегий суперпользователя) Для установки и из­менения пароля используется команда passwd. Она имеет следующий синтаксис: Команда пытается вынудить пользователей вводить «сильные» пароли. Это оз­начает, что она будет отвергать слишком короткие пароли, слишком похожие на предыдущие пароли, пароли, являющиеся словарными словами или легко угады­ваемые:[me@linuxbox ~]$ passwd Смена пароля для me. При наличии привилегий суперпользователя можно передать команде passwd ар­гумент с именем пользователя, чтобы установить пароль для этого пользователя Суперпользователю доступна также возможность блокировки учетных записей, установки времени действия пароля и многое другое За подробностями обращай­тесь к странице справочного руководства (man) для команды passwd. Современные операционные системы обычно являются многозадачными, в том смыс­ле, что создают иллюзию одновременного решения множества задач, быстро пере­ключаясь с выполнения одной программы на другую. Ядро Linux управляет всем этим посредством процессов. Именно с помощью процессов Linux организует при­остановку программ в ожидании, пока наступит их очередь использовать процессор. Иногда компьютер становится «вялым», или приложение вообще перестает от­кликаться на команды пользователя Сейчас мы познакомимся с некоторыми ин­струментами командной строки, позволяющими увидеть, что делают программы, и завершить процессы, вышедшие из-под контроля. В момент запуска системы ядро инициирует выполнение нескольких собственных задач в виде процессов и запускает программу с названием init. В свою очередь init выполняет последовательность сценариев командной оболочки (находятсяКак действует процесс в /etc), называемых сценариями начальной загрузки (init scripts), которые запу­скают все системные службы. Многие из этих служб реализованы как программы - демоны (daemon programs), то есть программы, действующие в фоновом режиме и выполняющие свою работу без участия пользователя. Поэтому, даже в отсут­ствие зарегистрированных пользователей система выполняет определенные слу­жебные процедуры Принцип, по которому программа может запускать другие программы, выражает­ся правилом: родительский процесс запускает дочерний процесс. Ядро хранит информацию обо всех процессах, чтобы упорядочить их работу. Например, каждому процессу присваивается номер, который называют идентифи­катором процесса (Process ID, PID) . Идентификаторы процессов присваиваются в порядке возрастания, при этом процесс init всегда получает идентификатор PID 1. Ядро также следит за памятью, выделенной каждому процессу, и за готов­ностью процессов возобновить выполнение Подобно файлам, процессы также имеют идентификаторы владельца и пользователя, эффективный (или действую­щий) идентификатор пользователя и т д

Просмотр списка процессов с помощью ps

Чаще всего для просмотра списка процессов используется команда ps Програм­ма ps имеет множество параметров, но в самом простейшем случае она использу­ется следующим образом: В этом примере команда вывела список с двумя процессами: процесс 5198 и процесс 10129 — программы bash и ps соответственно. Как можно заметить, по умол­чанию ps выводит не очень много информации, только процессы, связанные с текущим сеансом Чтобы увидеть больше, следует передать дополнительные параметры, но прежде чем мы сделаем это, давайте рассмотрим другие поля в вы­воде команды ps. Поле TTY — это сокращение от teletype (телетайп), оно содержит информацию об управляющем терминале процесса. В Unix в этом поле выводится тип терминала. Поле TIME содержит объем процессорного времени, потребленного процессом. Как видите, ни один из процессов не является слишком обременитель­ным для компьютера Если добавить параметр x, можно получить более богатую информацию о проис­ходящем в системе: и еще много других процессов... Дополнительный параметр x (обратите внимание на отсутствие дефиса) сообщает команде ps, что та должна вывести все процессы, независимо от того, какие терминалы (если таковые имеются) управляют ими. Символ? в поле TTY указывает на отсутствие управляющего терминала. Таким образом, параметр x позволяет уви­деть все процессы в системе, которыми мы владеем Так как в системе одновременно выполняется множество процессов, ps произво­дит довольно длинные списки. Часто бывает полезно передать вывод ps команде less через конвейер, чтобы его проще было просматривать. Некоторые комбина­ции параметров приводят к выводу очень длинных строк, поэтому нелишним бу­дет также распахнуть окно эмулятора терминала на весь экран В этом примере в выводе появился новый столбец — STAT. Название STAT — это сокращение от state (состояние), столбец содержит информацию о текущем со­стоянии процесса, как показано в

Символ, описывающий состояние процесса, может сопровождаться другими сим­волами. Они отражают некоторые экзотические характеристики процессов. За дополнительной информацией обращайтесь к странице справочного руководства (man) для ps. Еще одна популярная комбинация параметров — aux (без дефиса в начале) . Она позволяет получить еще больше информации: и еще много других процессов...Эта комбинация параметров выводит процессы, принадлежащие всем пользова­телям. При использовании параметров без начального дефиса команда действует «в стиле BSD». Linux-версия команды ps может имитировать поведение програм­мы ps, используемой в некоторых реализациях Unix. С помощью этих параметров мы получили дополнительные столбцы, описанные Команда ps предоставляет массу информации о том, что делается в компьютере, но она дает только мгновенный снимок, то есть возвращаемая ею информация действительна лишь на момент вызова команды. Чтобы увидеть работу компью­тера в динамике, воспользуемся командой top: Программа top постоянно обновляет информацию о процессах (по умолчанию с периодом, равным 3 секундам), чтобы показать их активность с течением време­ни. Имя программы top отражает тот факт, что она используется для просмотра «топа» (наиболее активных) процессов в системе. Вывод команды top делится на две части: сводная информация о системе и таблица процессов, отсортированных по потреблению ими процессора: Программа top принимает ряд команд с клавиатуры. Наибольший интерес пред­ставляет команда h, которая выводит экран со справочной информацией, и q, ко­торая завершает top Оба основных окружения рабочего стола включают приложения с графическим интерфейсом, отображающие аналогичную информацию (подобно тому, как это делает Task Manager (Диспетчер задач) в Windows), но я считаю, что top лучше сво­их аналогов с графическим интерфейсом, потому что она работает быстрее и по­требляет меньше системных ресурсов В конце концов, программа мониторинга системы не должна замедлять систему, за которой мы наблюдаем

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

Теперь, когда мы можем видеть процессы и наблюдать за ними, можно присту­пать к управлению ими. Роль подопытной морской свинки в наших эксперимен­тах исполнит маленькая программка xlogo. Программа xlogo — это демонстраци­онная программа, поставляемая в составе X Window System (механизм создания графического изображения на дисплее), которая просто отображает окно с лого­типом X. Для начала давайте познакомимся с объектом экспериментов: После ввода команды на экране должно появиться небольшое окно с логотипом В некоторых системах xlogo может выводить предупреждающее сообщение, но его можно смело игнорировать Чтобы убедиться, что xlogo работает, попробуйте изменить размеры ее окна. Если после изменения размеров содержимое окна перерисовывается, значит, програм­ма работает Заметили ли вы, что командная оболочка не вывела приглашения к вводу после выполнения команды? Это объясняется тем, что командная оболочка ждет, пока программа завершится То же самое происходило со всеми программами, которые мы запускали до сих пор Если закрыть окно xlogo, оболочка выведет приглаше­ние к вводу Давайте понаблюдаем, что происходит после запуска xlogo. Сначала введите команду xlogo и убедитесь, что программа работает Затем вернитесь в окно тер­минала и нажмите комбинацию Комбинация CTRL+C в терминале прерывает выполнение программы. Фактиче­ски мы вежливо попросили программу завершиться. После нажатия CTRL+C окно xlogo закроется и командная оболочка выведет приглашение к вводу Таким способом можно прервать выполнение многих (но не всех) программ командной строки Представьте, что нам потребовалось вернуться в командную оболочку, не преры­вая выполнения программы xlogo. Мы можем сделать это, переведя программу в фоновый режим работы. Считайте, что терминал имеет передний план (то, что видно на поверхности, например приглашение к вводу) и задний план (фон, то, что скрыто под поверхностью) . Чтобы запустить программу сразу в фоновом режиме, нужно добавить в конец команды символ амперсанда (&): После ввода такой команды на экране появится окно xlogo, а командная оболочка вернется в приглашение к вводу, но перед этим выведет таинственные числа. Это сообщение является частью механизма управления заданиями (job control) . Таким способом командная оболочка сообщает, что мы запустили задание с номером 1 ([1]) и оно получило идентификатор процесса PID 28236. Если теперь выполнить команду ps, можно увидеть этот процесс: Механизм управления заданиями также дает возможность вывести список зада­ний, запущенных в терминале Этот список можно получить командой jobs: Результаты показывают, что у нас имеется одно выполняющееся задание с номе­ром 1, которое было запущено командой xlogo &. Процесс в фоновом режиме не получает ввод с клавиатуры, в том числе не видит попыток прервать его комбинацией CTRL+C. Вернуть процесс на передний план можно командой fg, как в следующем примере:

За командой fg должен следовать знак процента и номер задания (эта комбина­ция называется спецификатором задания, или jobspec). Если имеется только одно фоновое задание, спецификатор можно опустить Теперь завершим xlogo вводом CTRL+C.

Приостановка процесса

Иногда необходимо приостановить процесс на время, не завершая его. Это часто делается с целью перевести процесс переднего плана в фоновый режим Чтобы приостановить процесс переднего плана, используйте комбинацию CTRL+Z. Давай­те попробуем. В командной строке введите команду xlogo, нажмите ENTER, а затем комбинацию CTRL+Z: После приостановки xlogo убедимся, что программа действительно приостанови­лась, для этого попытаемся изменить размер окна xlogo. Увы, программа никак не реагирует на наши действия Далее можно или вернуть программу на передний план командой fg, или перевести ее в фоновый режим командой bg: Так же как в случае с командой fg, спецификатор задания можно опустить, если имеется только одно задание Возможность перевода в фоновый режим полезна и в том случае, если при запуске программы с графическим интерфейсом из командной строки вы забыли доба­вить в конец команды символ Зачем может понадобиться запускать программу с графическим интерфейсом из командной строки? Тому есть две причины. Во-первых, необходимая программа может отсутствовать в меню программ окружения рабочего стола (как, например, xlogo) Во-вторых, запуская программу из командной строки, можно увидеть сообщения об ошибках, которые невидимы, когда программа запускается из графического интерфейса. Иногда программа аварийно завершается при запуске из графическо­го меню В этом случае, запуская ее из командной строки, можно по сообщениям об ошибках понять причину аварии. Кроме того, некоторые программы с графиче­ским интерфейсом имеют интересные параметры командной строки Команда kill используется для «убийства» (kill), то есть для завершения про­цессов. Она позволяет принудительно завершить выполнение вышедшей из-под контроля программы, отвергающей любые другие попытки закрыть ее. Например: [me@linuxbox ~]$ xlogo & В этом случае сначала выполняется запуск программы xlogo в фоновом режиме В ответ командная оболочка выводит номер задания и идентификатор фонового процесса (PID) Далее вызывается команда kill, которой передается PID про­цесса, требующего завершения Процесс можно также идентифицировать, указав спецификатор задания (например, %1) вместо PID. Хотя все выглядит достаточно просто, в действительности команда kill не про­сто «убивает» (kill) процессы — она посылает им сигналы. Сигналы — один из нескольких способов, которыми операционная система общается с программа­ми Мы уже видели сигналы в действии на примере использования комбинаций клавиш CTRL+C и CTRL+Z Когда терминал принимает одну из этих комбинаций, он посылает сигнал программе на переднем плане В случае нажатия CTRL+C про­грамме посылается сигнал INT (Interrupt — прервать); в случае нажатия CTRL+Z по­сылается сигнал TSTP (Terminal Stop — сигнал «стоп» с клавиатуры) . Программы в свою очередь, принимают сигналы и могут реагировать на них. Эта возможность позволяет программе выполнить некоторые операции, например сохранить про­межуточные результаты, при получении сигнала на завершение

Посылка сигналов процессам командой kill

Наиболее типичный синтаксис команды kill имеет следующий вид: kill [-сигнал] PID...

Если сигнал явно не указан в команде, по умолчанию посылается сигнал TERM (terminate — завершить) . Команда kill чаще всего используется для посылки сиг­налов, перечисленных в табл 1 hup Обрыв связи. Это пережиток старых добрых времен, когда терминалы подключались к удаленным компьютерам посредством телефонных ли­ний и модемов. Этот сигнал используется, чтобы подсказать программе, что потеряна связь с управляющим терминалом. Действие этого сигнала можно продемонстрировать, закрыв окно терминала. Программа перед­него плана, запущенная в терминале, получит сигнал и завершится. Этот сигнал также используется многими программами-демонами для повторной инициализации. То есть когда программа-демон получает этот сигнал, она перезапускается и повторно читает свои конфигураци­онные файлы. Веб-сервер Apache, например, как раз такая программа - демон, она именно так реагирует на сигнал HUP Поэкспериментируем с командой kill: Здесь мы запустили программу xlogo в фоновом режиме и затем с помощью команды kill послали ей сигнал HUP. Программа xlogo завершилась, и командная оболочка сообщила, что фоновый процесс принял сигнал разрыва связи Иногда необходимо нажать клавишу ENTER пару раз, чтобы увидеть сообщение. Обратите внимание, что сигнал можно указать по номеру или по имени, включая имена сиг­налов, начинающиеся с префикса SIG:

Повторите пример, приведенный выше, и попробуйте послать другие сигналы Имейте в виду, что вместо PID можно также передавать спецификатор задания Процессы, подобно файлам, имеют владельцев, и чтобы послать сигнал процессу командой kill, вы должны быть владельцем процесса (или суперпользователем) . Помимо сигналов, наиболее часто используемых с командой kill и перечис­ленных система часто использует другие сигналы, перечисленные. Любопытные пользователи могут получить полный список сигналов, выполнив следующую команду: Кроме того, существует возможность с помощью команды killall послать сигнал сразу нескольким процессам, соответствующим указанной программе или имени пользователя Она имеет следующий синтаксис: Помните: так же как при использовании команды kill, вы должны обладать при­вилегиями суперпользователя, чтобы посылать сигналы процессам, которыми не владеете Так как мониторинг процессов является одной из важнейших задач системно­го администрирования, существует множество команд, помогающих в этом перечислены некоторые из них, с ними вы можете поэксперименти­ровать Как обсуждалось выше, командная оболочка на протяжении всего сеанса работы использует массу информации, которая называется окружением. Данные, храня­щиеся в окружении, используются программами для выяснения деталей конфи­гурации Даже при том, что для хранения своих настроек большинство программ использу­ет конфигурационные файлы, некоторые программы также учитывают значения, хранящиеся в окружении. Зная это, можно использовать окружение для настрой­ки некоторых параметров командной оболочки

Что хранится в окружении?

Командная оболочка хранит в окружении данные двух основных типов, хотя bash практически не делает различий между типами. Эти данные хранятся в пере­менных окружения и в переменных командной оболочки. Переменные командной оболочки — это фрагменты данных, инициализируемые командой bash, а пере­менные окружения — практически все остальное Помимо переменных команд­ная оболочка хранит также программируемые данные, а именно псевдонимы и функции командной оболочки. Мы уже познакомились с псевдонимами в главе 5, а о функциях (которые имеют отношение к сценариям командной оболочки) по­говорим в части IV книги. Увидеть, что хранится в окружении, можно при помощи встроенной в bash коман­ды set или программы printenv. Команда set выводит переменные обоих ви­дов — командной оболочки и окружения, — тогда как printenv выводит только последние Так как список содержимого окружения очень велик, его лучше про­сматривать, передавая вывод любой из команд по конвейеру в less: Это список переменных окружения с их значениями. Например, в списке можно увидеть переменную с именем USER, содержащую значение me. Команда printenv может также вывести значение конкретной переменной: Команда set при вызове без параметров и аргументов выводит переменные обоих типов — командной оболочки и окружения, — а также все объявленные функции командной оболочки Единственный элемент окружения, который не выводится командами set и printenv, это псевдонимы. Чтобы вывести список псевдонимов, используйте команду alias без аргументов: Окружение содержит довольно много переменных, и хотя ваше окружение может отличаться от представленного здесь, вы почти наверняка увидите у себя пере­менные, перечисленные Не волнуйтесь, если какие-то переменные у вас отсутствуют. Они могут отличать­ся в разных дистрибутивах Когда мы входим в систему, запускается программа bash и читает содержимое се­рии конфигурационных сценариев, называемых файлами запуска (startup files), где определяется окружение по умолчанию, общее для всех пользователей Затем она читает дополнительные файлы запуска в вашем домашнем каталоге, где опре­деляется личное окружение. Точная последовательность обработки файлов зави­сит от типа запускаемого сеанса командной оболочки Сеансы работы с командной оболочкой входа могут быть двух типов: сеанс ко­мандной оболочки входа (login shell session) и сеанс простой командной оболочки (non-login shell session) Сеанс командной оболочки входа (login shell session) — это сеанс, который на вхо­де запрашивает имя пользователя и пароль, например, когда вход выполняется в виртуальной консоли. Сеанс простой командной оболочки (non-login shell session) обычно начинается, когда запускается терминал в графическом окружении Командные оболочки входа читают один или несколько файлов запуска, перечис­ленных в табл. Помимо чтения настроек из файлов запуска, перечисленных выше, обычные ко­мандные оболочки наследуют окружение от родительского процесса, каковым обычно является командная оболочка входа Загляните в свою систему и посмотрите, какие файлы запуска у вас имеются Помните: поскольку большинство имен файлов из перечисленных выше начина­ется с точки (такие файлы считаются скрытыми), при использовании команды ls ей необходимо передавать параметр –a С точки зрения обычного пользователя, файл ~/.bashrc является, пожалуй, са­мым важным файлом запуска, потому что его содержимое читается практически всегда Обычные командные оболочки читают его по умолчанию, а большинство файлов запуска для командных оболочек входа написаны так, что оболочка также прочитает файл ~/.bashrc.

Что находится в файлах запуска?

Если заглянуть внутрь типичного файла. bash_profile (взятого из системы CentOS-4), можно увидеть следующее: Строки, начинающиеся с #, — это комментарии, они не читаются командной обо­лочкой, а предназначены для человека. Первый интересный фрагмент начинается в четвертой строке: Этот код называется составной условной командой, полное описание которой бу­дет дано в части IV книги, где обсуждается программирование на языке команд­ной оболочки, а пока приведем ее перевод на человеческий язык: Если файл "~/.bashrc" существует, тогда прочитать файл "~/.bashrc" file. Как видите, этот фрагмент вынуждает командную оболочку входа прочитать со­держимое файла. bashrc. Следующая операция, выполняемая в файле запуска, имеет отношение к переменной PATH. Приходилось ли вам задумываться над тем, как командная оболочка находит команды, которые вводятся в командной строке? Например, когда мы вводим ls, командная оболочка не обыскивает весь компьютер целиком, чтобы найти /bin/ls (полный путь к команде ls), а просматривает только каталоги, перечисленные в переменной PATH Переменная PATH часто (но не всегда, в зависимости от дистрибутива) устанавли­вается в файле запуска /etc/profile, как показано ниже: Здесь в конец списка в переменной PATH добавляется каталог $HOME/bin. Этот код может служить примером использования механизма подстановки параме­тров, с которым мы познакомились в главе 7 . Для демонстрации попробуйте вы­полнить следующий пример: Используя этот прием, можно добавлять текст в конец содержимого переменной. При добавлении строки $HOME/bin в конец содержимого переменной PATH про­исходит добавление каталога $HOME/bin в список каталогов, где будет выпол­няться поиск вводимых команд Это означает, что если мы решим создать каталог в своем домашнем каталоге для хранения личных программ, командная оболочка уже будет готова к этому Нам останется только дать имя bin этому каталогу Наконец, у нас осталась еще одна строка: export PATH Команда export указывает командной оболочке сделать содержимое переменной PATH доступным дочерним процессам этой оболочки

Теперь, зная, где находятся файлы запуска и что они содержат, мы можем изме­нить их, чтобы настроить окружение Как правило, изменение содержимого переменой PATH или определение допол­нительных переменных окружения следует производить в файле. bash_profile (или эквивалентном ему, в зависимости от дистрибутива, — например, в Ubuntu используется файл. profile) . Во всех остальных случаях изменения должны про­изводиться в. bashrc Если вы не системный администратор и вам не требуется вносить изменения, касающиеся всех пользователей системы, изменяйте только файлы в своем домашнем каталоге. Конечно, можно изменять файлы в /etc, такие как profile, и во многих случаях в этом есть определенный смысл, но давайте пока избегать рискованных действий

Текстовые редакторы

Для редактирования (то есть изменения) файлов запуска командной оболочки, а также большинства других конфигурационных файлов в системе использует­ся программа, которая называется текстовым редактором. Текстовый редак­тор, подобно текстовому процессору, позволяет редактировать слова на экране, перемещая курсор От текстового процессора эта программа отличается только поддержкой простого текста и нередко наличием особенностей, необходимых при разработке программ. Текстовые редакторы — основной инструмент, использу­емый программистами для создания программного кода и системными админи­страторами для управления конфигурационными файлами, определяющими на­стройки системы Для Linux существует огромное число текстовых редакторов; в вашей системе почти наверняка установлено несколько из них Почему было создано так много редакторов? Вероятно, потому, что программистам нравится писать их, а так как программисты очень активно пользуются редакторами, они стремятся воплотить в них свои взгляды на то, как должны работать эти редакторы Неважно, как вы назовете файл с резервной копией; просто дайте ему такое имя, чтобы было понятно, что это за файл Наиболее часто для имен файлов с резерв­ными копиями используются расширения. bak, .sav, .old и. orig. Да, и не забудьте, что команда cp без лишних вопросов затирает существующие файлы. Экран редактора делится на три части: заголовок в верхней части, область редак­тирования текста в середине и меню команд внизу Так как nano проектировался как замена текстового редактора, входящего в состав почтового клиента, он не об­ладает развитыми функциями редактирования Первая команда, которую нужно узнать при использовании любого редакто­ра, — это команда выхода из программы. Чтобы покинуть nano, нажмите CTRL+X. Эта команда присутствует в меню, в нижней части экрана. Нотация ЛХ означаетCTRL+X Это распространенная форма записи управляющих комбинаций, исполь­зуемая во многих программах Вторая команда, которую следует знать, — как сохранить изменения В nano со­хранение выполняется нажатием CTRL+O. Теперь, обладая новыми знаниями, при­ступим к правке текста. Используя клавишу со стрелкой вниз и/или Page Down, переместите курсор в конец файла и добавьте в. bashrc следующие строки: Как видите, догадаться о назначении многих новых строк непросто, поэтому нелиш­ним будет снабдить их комментариями, чтобы прояснить смысл для тех, кто будет читать файл. bashrc Используя редактор, добавьте пояснения, как показано ниже: Так намного лучше! Закончив правку, нажмите CTRL+O, чтобы сохранить изменен­ный файл. bashrc, и CTRL+X, чтобы выйти из nano. Всякий раз, изменяя конфигурационные файлы, добавляйте краткие комментарии, описывающие эти изменения. Вне всяких сомнений, вы будете помнить назначение своих изменений завтра, но вспомните ли вы об этом через шесть месяцев? Сделайте себе подарок, добавьте несколько комментариев. Кроме того, хорошо бы завести файл журнала и в нем фиксировать произведенные изменения. Комментарии в сценариях на языке командной оболочки и в файлах запуска начинаются с символа #. В других конфигурационных файлах для этой цели могут использоваться другие символы. Комментарии можно найти в большинстве конфигурационных файлов. Используйте их как руководство. В конфигурационных файлах вам часто будут встречаться закомментированные стро­ки, чтобы предотвратить их влияние на соответствующую программу. Это делается с целью показать читателю возможные варианты настройки или примеры правильного синтаксиса оформления настроек. Например, файл. bashrc в Ubuntu 8.04 содержит следующие строки: Последние три строки — это допустимые определения псевдонимов, только закоммен­тированные. Если удалить начальные символы # из этих трех строк (это называется раскомментировать), псевдонимы будут активированы. Напротив, если добавить символ # в начало строки, можно деактивировать конфигурационную строку, сохранив информацию, хранящуюся в ней. Изменения, произведенные в файле. bashrc, не вступят в силу, пока вы не закроете терминал и не запустите новый, потому что оболочка читает содержимое файла. bashrc только в начале сеанса Однако существует возможность принудить bash повторно прочитать измененный файл. bashrc следующей командой: [me@linuxbox ~]$ source. bashrc После этого изменения должны вступить в силу Попробуйте, например, один из новых псевдонимов.

Освоение командной строки Linux

Освоение командной строки Linux, как становление пианиста-виртуоза, невоз­можно за один день Для этого требуются годы практики В этой главе вы позна­комитесь с текстовым редактором vi (произносится как «ви ай»), одной из тради­ционных программ Unix. Редактор vi известен своим сложным пользовательским интерфейсом, но когда вы увидите, как мастер садится за клавиатуру и начинает «играть», вы станете свидетелем высокого искусства Вы не станете мастерами, прочитав эту главу, но закончив ее, вы будет знать, как сыграть «Собачий вальс» на vi Зачем в современном мире редакторов с графическим интерфейсом и простых в использовании редакторов с текстовым интерфейсом, таких как nano, осваивать vi? На то есть три веские причины: vi всегда под рукой. Он может прийти на помощь в системах, где отсутству­ет графический интерфейс, например на удаленном сервере или в локальной системе с нерабочей конфигурацией X. Редактор nano, хотя и чрезвычайно по­пулярен, все же недостаточно универсален. POSIX, стандарт программной со­вместимости систем Unix, требует наличия в них vi vi легковесный и быстрый. Для многих задач гораздо проще запустить vi, чем найти в меню редактор с графическим интерфейсом и ждать, пока несколько мегабайтов загрузится в память Кроме того, vi специально проектировался для скоростного ввода с клавиатуры Как будет показано ниже, опытный поль­зователь vi никогда не отрывает рук от клавиатуры во время редактирования Мы не хотим, чтобы другие пользователи Linux и Unix считали нас неженками. Хорошо, пусть будет две веские причины. Первая версия vi была написана Билли Джоем, студентом Калифорнийско­го университета в городе Беркли, который позднее стал сооснователем Sun Microsystems в 1976 году. Название vi произошло от слова visual (экранный), по­тому что редактор предназначался для редактирования на экране видеотермина­ла с возможностью перемещения курсора по всей его поверхности До экранных редакторов существовали строчные редакторы, позволяющие редактировать текст только по одной строке Чтобы внести изменения в строчном редакторе, нужно было сначала перейти к требуемой строке а затем описать требуемое из­менение: добавление или удаление текста С появлением видеотерминалов (вза­мен терминалов с печатающим устройством, таких как телетайпы) стало воз­можным визуальное редактирование на экране В действительности vi включает в себя мощный строчный редактор ex, и его можно использовать для ввода ко­манд во время работы в vi Большинство дистрибутивов Linux содержат не настоящий редактор vi, а его улучшенную замену с именем vim (сокращенно от Vi IMproved — Vi улучшенный), созданную Брамом Моленаром (Bram Moolenaar) . vim существенно совершеннее традиционного редактора vi и в системах Linux обычно используется под симво­лической ссылкой (или псевдонимом) vi В обсуждении ниже будет предпола­гаться, что у вас есть программа с именем vi, которая в действительности является редактором vim Так же как при знакомстве с nano, которое произошло в предыдущей главе, снача­ла научимся выходить из редактора Для этого введите следующую команду (об­ратите внимание: двоеточие — это часть команды): В окне терминала должно появиться приглашение к вводу командной оболочки Если по какой-то причине выход из vi не получился (скорее всего, потому что вы внесли какие-то изменения и еще не сохранили их), сообщите vi, что вы действи­тельно хотите выйти, добавив в команду восклицательный знак: Если вы «заблудились» в vi, попробуйте дважды нажать ESC, чтобы вернуться на верный путь

Режимы редактирования

Давайте снова запустим vi, но на этот раз укажем имя несуществующего файла Именно так можно с помощью vi создать новый файл: В случае успеха на экране должно появиться следующее: "foo. txt" [New File] Начальные символы тильды (~) сообщают об отсутствии текста в соответствую­щих строках. Таким способом vi сообщает нам, что файл пуст. Не вводите пока ничего! Вторая важная вещь, которую нужно усвоить (после того, как вы научились выходить), — vi является режимным редактором. Сразу после запуска vi оказывается в командном режиме В этом режиме практически каждая клавиша является командой, поэтому если вы начнете ввод, vi может запутаться сам и за­путать вас Чтобы добавить какой-то текст в файл, необходимо сначала перейти в режим вставки. Для этого нажмите клавишу I (i) . Вслед за этим, если vim работает в обыч­ном расширенном режиме, в нижней части экрана появится надпись (она не по­явится, если редактор работает в режиме совместимости с vi): nsert -­Теперь можно ввести какой-нибудь текст Попробуйте, например: Съешь же ещё этих мягких французских булок, да выпей чаю. Чтобы выйти из режима вставки и вернуться в командный режим, нажмите ESC. Чтобы сохранить изменения в файл, введите ex-команду, находясь в командном режиме Для этого нажмите клавишу : После этого в нижней части должен по­явиться символ двоеточия: Чтобы выполнить запись изменений в файл, вслед за двоеточием введите w и на­жмите ENTER: В начале этого раздела, где показан экран, который выводится сразу после запуска vim (взят из Ubuntu 8.04), можно заметить текст: «Running in Vi compatible mode» (запущен в режиме совместимости с vi). Это означает, что vim был запущен в ре­жиме, близко повторяющем обычное поведение vi, а не в расширенном режиме vim. Чтобы беспрепятственно следовать за дальнейшим обсуждением в этой главе, запустите vim в расширенном режиме. Для этого в вашем распоряжении имеется пара возможностей: запустить редактор командой vim вместо vi (если этот прием сработает, по­думайте о том, чтобы добавить псевдоним vi='vim' в свой файл. bashrc file); выполнить следующую команду, чтобы добавить строку в конфигурационный файл vim: В разных дистрибутивах Linux vim упакован по-разному. В некоторых дистрибутивах по умолчанию устанавливается минимальная версия vim, поддерживающая лишь ограниченный набор возможностей vim. Поэтому, выполняя примеры из этой главы, вы можете столкнуться с отсутствием некоторых возможностей, — в этом случае просто установите полную версию vim командой: sudo apt-get install vim. Находясь в командном режиме, vi предлагает большое число команд управления курсором, часть из которых также используется программой less. пе­речислены некоторые из этих команд Почему для перемещения курсора были выбраны клавиши H, J, K и L? Потому что, когда был написан редактор vi, не все видеотерминалы имели кнопки со стрел­ками на клавиатуре Таким образом, опытные пользователи, хорошо владеющие клавиатурой, могли управлять курсором, не отрывая пальцев от клавиш Многие команды в vi могут начинаться с числа, как команда G До­бавляя число в команду, можно указать, сколько раз она должна быть выполнена Например, команда 5j переместит курсор на пять строк вниз.

Основы редактирования

Редактирование в основном заключается в нескольких простых операциях, та­ких как вставка текста, удаление текста и перемещение фрагментов текста с при­менением операций вырезания и вставки Конечно же, vi поддерживает все эти операции своим неповторимым способом vi поддерживает ограниченную форму отмены. Если нажать клавишу U в командном режиме, vi отменит последнее вы­полненное изменение Это пригодится нам, когда мы будем пробовать некоторые простые команды редактирования Другой способ вставки текста — вставка строк. Он позволяет вставить пустую строку между двумя имеющимися строками и перейти в режим вставки Данный способ предлагает два варианта вставки, перечисленные Под третьей строкой появилась пустая строка, и редактор перешел в режим встав­ки. Выйдите из режима вставки нажатием ESC. Введите u, чтобы отменить измене­ния. Введите O, чтобы вставить пустую строку выше курсора: Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Как можно догадаться, vi предлагает несколько способов удаления текста, и все они требуют нажатия одной или двух клавиш. Первый способ: клавиша X удаляет символ в позиции курсора. Команде x может предшествовать число, определяю­щее количество удаляемых символов Клавиша D более универсальна Команде d также может предшествовать число, определяющее количество операций удале­ния. Кроме того, команда d всегда сопровождается командой перемещения курсо­ра, управляющей размером удаляемой области приводится несколько примеров команды удаления Поместите курсор на слово Это в первой строке. Вводите х, пока текст до конца предложения не будет удален. Затем введите несколько раз команду u, чтобы от­менить удаление Теперь давайте проведем операцию удаления еще раз, но на этот раз воспользуем­ся командой d. Снова установите курсор на слово Это и введите dW, чтобы удалить слово: Съешь же ещё этих мягких французских булок, да выпей чаю. классно. Команда d не просто удаляет текст, она «вырезает» его Каждый раз, когда выпол­няется команда d, удаленный текст копируется в буфер вставки (своего рода бу­фер обмена — clipboard), откуда позднее его можно извлечь командой p и вставить правее позиции курсора или левее — командой P. Давайте попробуем что-нибудь скопировать и вставить Поместите курсор на пер­вую строку и введите уу, чтобы скопировать текущую строку Далее, переместите курсор в последнюю строку (G) и введите p, чтобы вставить скопированную стро­ку ниже текущей: Для выполнения поиска с заменой (в vi эта операция называется подстановкой) в диапазоне строк или во всем файле vi использует ex-команды. Например, за­менить слово Строка словом строка во всем файле можно следующей командой: В команде подстановки можно указать, что она должна запрашивать подтвержде­ние у пользователя перед заменой. Для этого добавьте символ с в конец команды. Например: Эта команда вернет содержимое файла в прежнее состояние, но перед каждой за­меной vi будет останавливаться и спрашивать подтверждение, выдавая следую­щее сообщение: заменить на Строка? (y/n/a/q/l/AE/AY) В круглых скобках перечислены возможные варианты ответов, описание которых приводится.

Редактирование нескольких файлов

Иногда бывает необходимо редактировать сразу несколько файлов. Например, может понадобиться внести изменения в файлы или скопировать содержимое из одного файла в другой Редактор vi позволяет открыть несколько файлов, пере­числив их в командной строке: Давайте закроем текущий сеанс работы vi и создадим новый файл для редактиро­вания. Введите :wq, чтобы выйти из vi с сохранением изменений в тексте. Далее, создайте новый файл в домашнем каталоге, который мы будем использовать в на­ших экспериментах Создайте файл, захватив в него вывод команды ls: После запуска vi вы увидите на экране первый файл: Съешь же ещё этих мягких французских булок, да выпей чаю. Это классно. Чтобы переключиться с одного файла на следующий, выполните ex-команду: Чтобы вернуться обратно, в предыдущий файл, выполните: Теперь мы можем переключаться между файлами, но vi проводит политику, запре­щающую переключаться между файлами, если в текущем файле имеются несохра - ненные изменения Чтобы заставить vi переключиться между файлами с потерей всех несохраненных изменений, добавьте в команду восклицательный знак (!) . В дополнение к методам переключения между файлами, описанным выше, vim (и некоторые версии vi) предоставляет дополнительные ex-команды, упрощаю­щие управление множеством файлов. Например, командой :buffers можно вы­вести список редактируемых файлов В этом случае список появляется в нижней части экрана: Нажмите ENTER или введите команду для продолжения Чтобы перейти к другому буферу (файлу), введите :buffer и номер искомого бу­фера. Например, переключиться с буфера 1, содержащего файл foo. txt, на буфер 2, содержащий файл ls-output. txt, можно командой: после выполнения этой команды на экране появится второй файл Также существует возможность добавлять файлы в текущий сеанс редактирова­ния. Команда :e (сокращенно от edit — редактировать) с именем файла откроет дополнительный файл Завершите текущий сеанс редактирования и вернитесь в командную строку Запустите vi снова, но на этот раз с единственным файлом: Часто в процессе редактирования множества файлов бывает необходимо скопи­ровать фрагмент текста из одного файла в другой Это легко сделать с помощью обычных команд копирования и вставки, представленных выше Посмотрим, как можно это осуществить Сначала, в случае с использованием двух наших файлов, переключитесь на буфер 1 (foo. txt), выполнив команду: В результате на экране должно появиться следующее: Далее переместите курсор на первую строку и введите уу, чтобы скопировать строку Переключитесь на второй буфер командой: Переместите курсор на первую строку и вставьте строку, скопированную в преды­дущем файле, введя команду р: Кроме того, мы можем вставить файл целиком в другой файл. Для выполнения этого приема Команда :r (сокращенно от read — читать) вставит указанный файл перед позици­ей курсора. Теперь экран должен выглядеть так: И здесь vi предлагает нам несколько способов сохранения отредактированных файлов. Мы уже знакомы с ex-командой :w, но существуют и другие команды, ко­торые могут оказаться полезными В командном режиме можно ввести ZZ, чтобы сохранить текущий файл и выйти из vi. Аналогично, ex-команда :wq сочетает в себе команды :w и :q, первая из которых сохраняет файл, а вторая закрывает редактор

В команде :w можно также указать имя файла. В этом случае она будет действо­вать как команда Save As (Сохранить как) . Например, если вы редактируете foo. txt и хотите сохранить альтернативную версию с именем foo1.txt, введите следующую команду.

Настройка приглашения к вводу

В этой главе мы рассмотрим, казалось бы, такую незначительную деталь, как при­глашение к вводу командной оболочки (prompt) . Кроме того, мы познакомимся с некоторыми внутренними особенностями работы командной оболочки и самой программы эмулятора терминала Как и многое в Linux, приглашение к вводу командной оболочки можно настраивать в очень широких пределах, и хотя мы принимаем это приглашение как дан­ность, оно в действительности оказывается очень полезным средством для тех, кто умеет управлять им По умолчанию строка приглашения к вводу имеет следующий вид: Имея список специальных символов, можно попробовать изменить оформле­ние приглашения Для начала сохраните исходное определение, чтобы его мож­но было восстановить позднее Для этого скопируйте значение переменной PS1 в другую переменную: Здесь создается новая переменная с именем ps1_old, и ей присваивается значение переменной PS1 Убедиться, что значение скопировано, можно с помощью коман­ды echo: Это позволит вам в любой момент восстановить исходное оформление приглаше­ния, выполнив обратную процедуру: Теперь, когда все готово, давайте посмотрим, что получится, если определить пу­стую строку приглашения: Если определить приглашение как пустую строку, мы ничего не увидим Стро­ка приглашения просто исчезнет! В действительности она все еще существует, но поскольку она пустая, на экране ничего не отображается, — собственно, как мы и просили Так как пустая строка приглашения дезориентирует, давайте опреде­лим минимальное оформление: Так лучше По крайней мере, теперь видно, где мы находимся Обратите внимание на завершающий пробел внутри кавычек Он обеспечивает дополнительное про­странство на экране между знаком доллара и курсором Теперь при каждом выводе строки приглашения вы должны слышать короткий звуковой сигнал Постоянно звучащий сигнал может раздражать, но в некоторых случаях он может быть полезен, например если нужно получать звуковое опове­щение об удачном завершении долго выполняющихся команд А теперь попробуйте сделать приглашение более информативным, добавив имя хоста и время суток: Добавление времени суток может пригодиться, если есть необходимость зафикси­ровать, в какой момент закончилось выполнение задачи Наконец, сделайте при­глашение похожим на оригинальное: Попробуйте использовать другие последовательности и посмотрите, сможете ли вы получить свою уникальную строку приглашения к вводу. Большинство программ эмуляторов терминалов реагируют на некоторые по­следовательности непечатаемых символов, например управляющие атрибутами символов (такими, как цвет, жирность и мигание) и позицией курсора. О позиции курсора мы поговорим чуть позже, а сейчас займемся цветом В стародавние времена, когда дискеты были большими, а терминалы подключались к удаленным компьютерам, существовало великое многообразие моделей терминалов, и все они работали по-разному. Они имели разные клавиатуры и по-разному интерпре­тировали управляющую информацию. В Unix и в Unix-подобных системах имеются две очень сложные подсистемы (которые называются termcap и terminfo), решающие все проблемы, связанные с управлением терминалами. Если заглянуть в самые потаенные кладовые настроек эмулятора терминала, можно обнаружить параметр, определяющий тип эмулируемого терминала. Чтобы заставить терминалы говорить на едином языке, Американский национальный институт стандартов (American National Standards Institute, ANSI) разработал набор последовательностей символов для управления видеотерминалами. Заслуженные поль­зователи DOS еще помнят файл ANSI. SYS, который применялся для интерпретации этих последовательностей. Цветом символов можно управлять, посылая эмулятору терминала экранирован­ные последовательности ANSI внутри потока символов, предназначенных для вы­вода на экран. Экранированные последовательности не «выводятся» на экран; они интерпретируются терминалом как инструкции. Как показано в табл. 13 .1, для включения непечатаемых символов используются последовательности \[ и \] Экранированные последовательности ANSI начинаются с восьмеричного кода 033 (код, генерируемый клавишей ESC), за которым следует необязательный атрибут символа и инструкция Например, вот как выглядит код, определяющий текст как простой (атрибут = 0), черного цвета \033[0;30m перечислены поддерживаемые цвета текста. Обратите внимание, что цвета делятся на две группы, отличаясь наличием атрибута жирного текста (1), из-за которого возникает впечатление более «светлых» (light) цветов.

Дистрибутив Linux

Общаясь с другими членами сообщества Linux, мы услышим массу мнений о том, какой дистрибутив Linux лучше. Часто обсуждения дистрибутивов выглядят до­вольно глупыми, скатываясь к сравнению, например, привлекательности обоев рабочего стола (некоторые отвергают Ubuntu, потому что им не нравится цвето­вая схема по умолчанию!) и других тривиальных особенностей. Самой важной отличительной чертой дистрибутива является система управления пакетами и активность сообщества, поддерживающего дистрибутив Поработав с Linux достаточно долгое время, легко заметить, насколько динамичен программ­ный ландшафт этой системы. Он находится в постоянном движении. Большин­ство создателей основных дистрибутивов Linux выпускают новые версии каждые шесть месяцев, а множество отдельных программ обновляется каждый день Что­бы не отставать от этой лавины программного обеспечения, нам нужен хороший инструмент для управления пакетами Управление пакетами (package management) — это методика установки и управления программным обеспечением в системе. В наши дни большинство может удовлетворить все свои потребности в программном обеспечении, устанавливая па­кеты, подготовленные создателями соответствующих дистрибутивов Linux. Это отличается от ситуации, возникшей в первые годы развития Linux, когда для уста­новки программ требовалось загружать и компилировать исходный код. Нельзя сказать, что было бы неправильно устанавливать программы из исходных кодов; в действительности наличие доступа к исходному коду является самым большим достоинством в Linux Это предоставляет возможность исследовать и улучшать систему Просто работать с заранее скомпилированными пакетами проще и бы­стрее В этой главе мы рассмотрим некоторые инструменты командной строки, исполь­зуемые для управления пакетами В то время как все основные дистрибутивы предоставляют мощные и современные программы с графическим интерфейсом для управления системой, умение работать с программами командной строки по-прежнему востребовано Они способны выполнять задачи, многие из которых сложно (если вообще возможно) выполнить с использованием родственных им программ с графическим интерфейсом Разные дистрибутивы используют различные системы пакетов, и, как правило, пакеты, подготовленные для одного дистрибутива, несовместимы с другими В большинстве дистрибутивов используется одна из двух основных технологий упаковки: разработанная создателями дистрибутива Debian с пакетами. deb и разработанная создателями дистрибутива Red Hat с пакетами. rpm. Существует не­сколько важных исключений, таких как Gentoo, Slackware и Foresight, но в боль­шинстве других дистрибутивов используется одна из двух основных систем, что показано Способ распространения программ, используемый в индустрии патентованного программного обеспечения, обычно включает покупку установочного носителя, такого как «установочный диск», и последующий запуск мастера установки ново­го приложения в систему Linux действует иначе. Практически все программное обеспечение для системы Linux находится в Интернете. Большая его часть предоставляется создателями дистрибутивов в форме файлов пакетов, а остальная часть доступна в исходном коде, который можно установить вручную Мы еще поговорим об установке про­грамм путем компиляции исходного

Файлы пакетов

Основной единицей программного обеспечения в системе пакетов является файл пакета. Файл пакета — это сжатая коллекция файлов, составляющих программный пакет Пакет может состоять из множества программ и файлов с данными, под­держивающих программы Помимо файлов для установки, файл пакета включает также метаданные с информацией о пакете, например текстовым описанием па­кета и его содержимого. Дополнительно многие пакеты включают сценарии для выполнения настроек до и после установки пакета

Файлы пакетов создаются людьми, ответственными за сопровождение пакетов (package maintainer), часто (но не всегда) являющимися сотрудниками компании - производителя дистрибутива Ответственный за пакет получает программное обеспечение в исходном коде от поставщика (автора программы), компилирует его и создает метаданные для пакета вместе со всеми необходимыми сценария­ми установки Часто ответственный за сопровождение пакета вносит изменения в оригинальный исходный код с целью улучшения интеграции программы с дру­гими компонентами дистрибутива Linux Некоторые проекты самостоятельно создают пакеты и дистрибутивы своего про­граммного обеспечения, и все же большинство пакетов в наше время собирается создателями дистрибутивов и заинтересованными третьими сторонами. Готовые пакеты помещаются в центральный репозиторий дистрибутива, где они становят­ся доступными для пользователей. Репозиторий может содержать тысячи паке­тов, специально собранных для дистрибутива Для дистрибутива может поддерживаться несколько разных репозиториев для программного обеспечения, находящегося на разных этапах разработки Напри­мер, дистрибутивы обычно имеют тестовый репозиторий, содержащий недавно созданные пакеты, которые предназначены для смельчаков, пытающихся оты­скать ошибки до того, как пакеты попадут в основной дистрибутив Нередко дис­трибутивы имеют репозиторий для разработки, куда помещаются пакеты, про­должающие разрабатываться и предназначенные для включения в ближайший выпуск дистрибутива Дистрибутив может также иметь сторонние репозитории Они необходимы для распространения программного обеспечения, которое по юридическим причинам, связанным с патентами или законами об управлении цифровыми правами (Digital Rights Management, DRM), не может быть включено в дистрибутив. Самым известным случаем является поддержка шифрования DVD, которая считается не­законной в Соединенных Штатах. Сторонние репозитории располагаются в стра­нах, где патенты или законы, ограничивающие распространение программного обеспечения, отсутствуют или действуют иначе Эти репозитории обычно полно­стью независимы от поддерживаемого ими дистрибутива, и для их использования нужно знать об их существовании и вручную включать их в конфигурационные файлы с настройками системы управления пакетами Программы редко действуют в одиночку; чаще они полагаются на наличие других программных компонентов. Стандартные операции, такие как ввод/вывод, на­пример, выполняются процедурами, которые совместно используются многими программами. Эти процедуры хранятся в так называемых разделяемых библиоте­ках (shared libraries), предоставляющих важные услуги нескольким программам. Если пакету требуется некий общий ресурс, такой как разделяемая библиотека, про него говорят, что он имеет зависимость. Современные системы управления пакетами поддерживают некоторые методы разрешения зависимостей, — это га­рантирует, что после установки пакета в системе будут также установлены все его зависимости

Высоко и низкоуровневые инструменты управления пакетами

Системы управления пакетами обычно включают инструменты двух типов: низ­коуровневые инструменты, решающие такие задачи, как установка и удаление файлов пакетов, и высокоуровневые инструменты, выполняющие поиск в мета­данных и разрешение зависимостей. В этой главе мы посмотрим, какие инструмен­ты входят в состав систем на основе Debian (таких, как Ubuntu и многих других), а также в состав последних продуктов Red Hat. Несмотря на то что все дистрибу­тивы на основе Red Hat опираются на одну и ту же низкоуровневую программу (rpm), они используют разные высокоуровневые инструменты. В ходе обсуждения мы познакомимся с высокоуровневой программой yum, используемой в дистрибу­тивах Fedora, Red Hat Enterprise Linux и CentOS. Другие дистрибутивы на основе Red Hat предоставляют высокоуровневые инструменты, сопоставимые по своим возможностям С помощью инструментов командной строки для управления пакетами можно выполнить множество разных операций. Мы рассмотрим наиболее типичные из них Вы должны знать, что низкоуровневые инструменты поддерживают также создание файлов пакетов, но эта тема выходит за рамки данной книги В следующем обсуждении под термином имя_пакета будет подразумеваться фак­тическое имя пакета, а под термином файл_пакета — имя файла пакета. Используя высокоуровневые инструменты для поиска метаданных в репозито­рии, можно найти пакет по его имени или описанию. Вот пример поиска текстового редактора emacs в системе Red Hat с помощью команды yum: yum search emac Высокоуровневые инструменты позволяют загрузить пакет из репозитория и установить его с полным разрешением всех зависимостей Вот пример установки текстового редактора emacs в системе Debian при помощи apt-get: apt-get update; apt-get install emacs Пакеты можно удалять с помощью и низкоуровневых, и высокоуровневых ин­струментов Примеры использования высокоуровневых инструментов приводят­ся Пример: удалить пакет emacs из системы Debian можно командой: apt-get remove emacs Наиболее типичной задачей управления пакетами является поддержание системы в актуальном состоянии обновлением пакетов до последних версий. Высокоуровне­вые инструменты способны выполнять эту важную задачу за один шаг Пример: следующая команда применит все обновления, доступные для пакетов, установленных в системе на основе Debian: Если обновленная версия пакета была загружена из источника, не являющегося репозиторием, ее можно установить, заменив предыдущую версию можно использовать для вывода списка всех пакетов, уста­новленных в системе В последующих главах мы исследуем множество программ, решающих широкий спектр прикладных задач. Хотя большинство этих программ обычно устанавлива­ется по умолчанию, иногда возникает необходимость установить дополнительные пакеты С вновь обретенными знаниями (и пониманием) особенностей управ­ления пакетами вы без труда сможете установить дополнительные программы и управлять ими. Те, кто прежде использовал другие платформы, иногда становятся жертвами мифов о сложности установки программного обеспечения в Linux и верят, что многообразие систем управления пакетами, используемых разными дистрибутивами, является серь­езной помехой. Вообще-то и правда — помехой, только не для пользователей, а для производителей патентованного программного обеспечения, желающих распространять свои программы только в виде двоичных файлов.

Программная экосистема Linux

Программная экосистема Linux основана на идеологии открытости исходного кода. Если разработчик программного обеспечения выпустит исходный код своего про­дукта, почти наверняка человек, связанный с дистрибутивом, упакует этот продукт и включит его в репозиторий. Этот подход гарантирует хорошую интеграцию продукта с дистрибутивом, и пользователь сможет получить все необходимое ему программ­ное обеспечение в одном месте, вместо того чтобы искать отдельные программы по разным веб-сайтам.

Драйверы устройств распространяются почти так же, только они не выделяются в от­дельные пакеты в репозитории дистрибутива, а включаются в ядро Linux. Можно сказать, что в Linux нет такого понятия, как «диск с драйверами». Либо ядро поддерживает данное устройство, либо нет, а ядро Linux поддерживает огромное число устройств. В действительности намного больше, чем Windows. Конечно, едва ли вас утешит ин­формация, что нужное вам устройство не поддерживается ядром. Однако если такое случится, ищите причину. Отсутствие драйвера поддержки обычно обусловлено одной из следующих причин: Устройство слишком новое. Так как многие производители аппаратного обеспечения не очень активно поддерживают Linux, задача написать драйвер для включения в ядро ложится на членов сообщества Linux. А это требует времени. Устройство слишком экзотическое. Не все дистрибутивы включают все возможные драйверы устройств. Для каждого дистрибутива настраивается свое ядро, и так как ядра настраиваются до мелочей (благодаря чему от­крывается возможность использовать Linux в самых разных устройствах, от наручных часов до больших ЭВМ), создатели дистрибутива могли пропустить ваше устройство. Найдя и загрузив исходный код драйвера, вы (да, да — вы) сможете скомпилировать и установить драйвер самостоятельно. Это не очень Производители аппаратного обеспечения что-то скрывают. Произво­дитель не выпустил либо исходный код драйвера для Linux, либо докумен­тацию, на основе которой можно было бы написать драйвер. Это означает, что производитель аппаратного обеспечения пытается сохранить программ­ные интерфейсы устройства в секрете. Так как мы предпочитаем не исполь­зовать засекреченные устройства в своих компьютерах, я предлагаю удалить это нетолерантное устройство и отправить его в кучу из других бесполезных гаджетов. В предыдущих главах мы познакомились с приемами работы с данными на уров­не файлов Для выполнения упражнений к этой главе нам понадобится флеш-диск (флешка), подключаемый к порту USB компьютера, диск CD-RW (для систем, оборудован­ных пишущим приводом CD-ROM) и такой раритет, как гибкий диск (опять же, если система оборудована этим устройством) Последние достижения Linux на настольных компьютерах сделали управление устройствами хранения чрезвычайно простым для обычных пользователей До­статочно подключить устройство к компьютеру, и оно тут же готово к работе. Раньше (года этак до 2004-го) все необходимые операции требовалось выпол­нять вручную В серверных системах эти операции по большей части все еще вы­полняются вручную, потому что серверы часто предъявляют особые требования к устройствам хранения и настройкам Первый шаг в управлении устройствами хранения — подключение самого устройства к дереву файловой системы Этот процесс называется монтировани­ем и позволяет устройству участвовать в работе операционной системы Как рас­сказывалось в главе 2, Unix-подобные операционные системы, такие как Linux, поддерживают единое дерево файловой системы, к разным точкам которого под­ключаются дополнительные устройства Этот подход отличается от используемо­го в MS-DOS и Windows, где каждому устройству соответствует отдельное дерево файлов и каталогов (например, C:\, D:\ и т. д.) .

В файле с именем /etc/fstab перечисляются устройства (обычно разделы жестко­го диска), монтируемые на этапе загрузки Ниже приводится пример содержимо­го /etc/fstab из системы Большинство файловых систем из перечисленных в приведенном примере явля­ются виртуальными, и наше обсуждение к ним неприменимо Наибольший инте­рес для нас в рамках исследования данной темы представляют первые три:

Просмотр списка смонтированных файловых систем

Для монтирования файловых систем используется команда mount. Если ввести команду без аргументов, она выведет список файловых систем, смонтированных в настоящий момент: Список имеет следующий формат: устройство on точка_монтирования type тип_файловой_системы (параметры). Например, первая строка соответствует устройству /dev/sda2, смонтированному как корневая файловая система типа ext3, доступная для чтения и записи (параметр rw) . В конце списка можно заме­тить две интересные записи Предпоследняя запись соответствует 2-гигабайтной SD-карте памяти в устройстве для чтения карт памяти, смонтированной в каталог Этот список получен в системе CentOS 5, где для создания корневой файловой системы используется диспетчер LVM. Подобно многим современным дистрибу­тивам Linux, эта система пытается автоматически монтировать компакт-диски Вставив в привод компакт-диск, мы увидим следующее: Это практически тот же список, с одной дополнительной записью Последняя запись в списке сообщает, что компакт-диск в приводе CD-ROM (устройство /dev/hdc в этой системе) смонтирован в каталог /media/live-1.0.10-8 и имеет файловую систему iso9660 (типичную для компакт-дисков) . Обратите внимание на имя устройства. Когда вы будете проводить эксперимент в своей системе, очень вероятно, что имя устройства у вас будет отличаться Теперь, когда мы знаем имя устройства для привода CD-ROM, размонтируем диск и повторно смонтируем его в другой каталог в дереве файловой системы. Для это­го необходимо получить права суперпользователя (способом, соответствующим вашей системе) и размонтировать диск командой umount: Следующий шаг: создать новую точку монтирования диска. Точка монтирования — это самый обычный каталог где-то в дереве файловой системы. В таком каталоге нет ничего необычного Он даже не должен быть пустым каталогом, правда, монтирова­ние устройства в непустой каталог сделает его прежнее содержимое недоступным, пока устройство не будет размонтировано. Итак, создаем новый каталог: Обратите внимание, что происходит при попытке размонтировать компакт-диск:

В чем причина? Устройство нельзя размонтировать, если оно используется каким - то пользователем или другим процессом В данном случае мы изменили текущий рабочий каталог, перенеся его в точку монтирования компакт-диска, что и стало причиной занятости устройства. Эту проблему легко исправить, перенеся текущий рабочий каталог куда-нибудь в другое место за пределами точки монтирования: Теперь устройство было успешно размонтировано. Если взглянуть на вывод команды free, показывающей статистику использования па­мяти, можно увидеть статистику с названием buffers (буферы). Компьютерные системы проектируются так, чтобы работать максимально быстро. Но медленные устройства препятствуют этому. Ярким примером служат принтеры. Даже самый быстрый принтер выглядит чрезвычайно медлительным по компьютерным стандартам. Компьютеры работали бы крайне медленно, если бы действительно были вынуждены ждать, пока принтер завершит печать страницы. В давние времена (когда персональные компьютеры еще не были многозадачными) это представляло настоящую проблему. При попытке распечатать электронную таблицу или текстовый документ компьютер мог стать недо­ступным до конца печати. Компьютер не мог посылать данные принтеру быстрее, чем тот мог их обработать, а принтеры не могли работать быстрее, потому что не могли быстро печатать. Эта проблема была решена созданием буфера печати, устройства, содержащего некоторый объем ОЗУ и находящегося между компьютером и принтером. При наличии буфера печати компьютер мог послать данные в буфер печати, который сохранял их в быстрой памяти ОЗУ, и компьютер возвращался к работе, не дожидаясь конца печати. В то же время буфер печати мог передавать данные принтеру из своей памяти со скоростью, приемлемой для принтера.

Идея буферизации

Идея буферизации широко используется для увеличения производительности компьюте­ров — необходимость работы с медленными устройствами не должна ухудшать произво­дительность системы. Операционные системы хранят данные, прочитанные с устройства и предназначенные для записи в устройство, так долго, насколько это возможно, и ис­пользуют их, прежде чем фактически обратиться к медленному устройству. В системе Linux, например, можно заметить, что при продолжительной работе она заполняет всю память. Это не означает, что Linux «использует» всю память, это означает лишь то, что Linux ис­пользует в своих интересах всю доступную память и буферизует как можно больше данных. Буферизация позволяет очень быстро выполнять запись в устройства хранения, по­тому что запись в физическое устройство откладывается «на потом». Данные, предна­значенные для устройства, накапливаются в памяти. Время от времени операционная система записывает эти данные в физическое устройство. Размонтирование устройства влечет за собой запись всех оставшихся данных в это устрой­ство, чтобы его можно было безопасно извлечь. Если носитель извлечь, не выполнив размонтирование, есть вероятность, что не все данные, предназначенные для устройства, будут записаны в него. Иногда эти данные могут включать жизненно важные обновления каталогов, отсутствие которых может привести к повреждению файловой системы — од­ной из самых больших неприятностей, которые могут случиться с компьютером. Иногда сложно определить название (имя) устройства В прошлом это было про­ще. Устройство всегда находилось в одном месте и никогда не менялось. Unix - подобные системы именно так и действовали Во времена, когда разрабатывалась система Unix, для «смены дискового устройства» требовалось использовать подъ­емник, чтобы извлечь из комнаты с ЭВМ устройство размером со стиральную ма­шину В последние годы типовая аппаратная конфигурация настольного компью­тера стала намного динамичнее, и система Linux вынуждена быть более гибкой, чем ее предшественницы В примерах, приведенных выше, мы использовали способность современной си­стемы Linux «как по волшебству» монтировать устройства, чтобы узнавать их названия постфактум. Но как быть тем, кто управляет сервером или каким-то другим окружением, где автоматическое монтирование не поддерживается? Как в этом случае определить название устройства?

Сначала давайте посмотрим, как система выбирает названия для устройств Если вывести содержимое каталога /dev (где живут все устройства), можно увидеть значительное число устройств: Кроме того, во многих системах можно увидеть такие символические ссылки, как /dev/cdrom, Если вам доведется работать в системе, которая не монтирует автоматически съемные носители, вы можете использовать следующий прием для определения названий таких устройств после их подключения Сначала запустите мониторинг содержимого файла /var/log/messages в режиме реального времени (для этого мо­гут потребоваться права суперпользователя): Эта команда выведет несколько последних строк из файла и приостановится Да­лее подключите извлекаемое устройство. В этом примере мы использовали 16-ме­габайтный флеш-диск Практически сразу же ядро обнаружит новое устройство и проверит его: Когда вывод опять приостановится, нажмите CTRL+C, чтобы вернуться в пригла­шение командной строки Наибольший интерес для нас представляют строки с упоминанием имени устройства [sdb], соответствующего нашим ожиданиям в отношении названия устройства диска SCSI. В этом отношении следующие две строки являются для нас особенно показательными: Они сообщают, что имя /dev/sdb соответствует всему устройству, а имя /dev/ sdb1 — первому разделу на этом устройстве. Как видите, работая с Linux, иногда приходится проводить массу интересных детективных расследований! Имя устройства сохраняется неизменным, пока оно остается физически подклю­ченным к компьютеру и до перезагрузки компьютера

Создание новых файловых систем

Представьте, что вам нужно отформатировать флеш-диск и вместо файловой си­стемы FAT32 создать на нем файловую систему, родную для Linux. Для этого сле­дует выполнить две операции: сначала (при необходимости) создать новое рас­пределение разделов, если имеющееся вас не устраивает, а затем создать новую, пустую файловую систему убедитесь, что используете имя устройства, верное для вашей системы, а не то, которое показано в примере. Игнорирование этого предупреждения может привести к форматированию (то есть к стиранию) другого диска! Программа fdisk позволяет напрямую выполнять низкоуровневые операции с дисковыми устройствами (такими, как жесткие диски и флеш-диски). С помо­щью этого инструмента можно изменять, удалять и создавать разделы на устрой­стве. Чтобы приступить к работе с флеш-диском, его нужно сначала размонтиро­вать (если прежде он был смонтирован) и затем запустить программу fdisk, как показано ниже: Обратите внимание, что здесь нужно указать имя, соответствующее устройству, целиком, без номера раздела После запуска программы вы увидите следующее приглашение: Script Обратите внимание, что устройство имеет объем 16 Мбайт и единственный раз­дел (1), занимающий 1006 цилиндров из 1008 доступных на устройстве. Раздел идентифицирован как раздел Windows 95 FAT32. Некоторые программы исполь­зуют этот идентификатор, ограничивая виды операций с диском, но чаще изме­нение идентификатора не влечет серьезных последствий Однако ради демон­страции мы изменим его, чтобы показать, что это раздел Linux Для этого нужно сначала узнать, какой идентификатор обозначает разделы Linux В листинге, приведенном выше, мы видели, что существующий раздел имеет идентифика­тор (столбец Id) b. Чтобы увидеть список известных типов разделов, вернитесь к меню программы и обратите внимание на пункт: l список известных типов разделов Если ввести команду l, появится длинный список допустимых типов разделов Среди них можно увидеть идентификатор b типа существующего раздела и иден­тификатор 83 для Linux. Вернемся обратно к меню программы, где можно увидеть команду изменения идентификатора раздела: Changed system type of partition 1 to 83 (Linux)Это все изменения, которые нам нужно было сделать До этого момента никаких изменений на самом устройстве не было произведено (все изменения пока просто зафиксированы в памяти программы, а не на физическом устройстве), поэтому теперь запишем измененную таблицу разделов на устройство и выйдем Если бы мы решили оставить устройство в неизменном состоянии, то могли бы вве­сти команду q и покинуть программу без записи изменений на устройство. Преду­преждающее сообщение, выглядящее зловещим, можно просто игнорировать. Завершив редактирование разделов (довольно простое, хотя так бывает не всег­да), мы создадим на флеш-диске новую файловую систему Для этого воспользу­емся программой mkfs (сокращенно от make filesystem — создать файловую систе­му), способной создавать разные файловые системы. Чтобы создать на устройстве файловую систему ext3, следует передать команде параметр - t и с типом файловой системы ext3, затем указать имя устройства и раздел, который требуется отформатировать: Эту процедуру с редактированием разделов и форматированием можно повторять с любыми дополнительными устройствами хранения, подключаемыми к системе Хотя в данном примере мы работали с маленьким флеш-диском, ту же процеду­ру можно применить и к внутренним жестким дискам, и к другим извлекаемым устройствам хранения, таким как жесткие USB-диски.

Проверка и восстановление файловой системы

Знакомясь с файлом/etc/fstab, мы видели некие странные цифры в конце каждой строки Каждый раз, когда система загружается, она проверяет целостность фай­ловых систем перед их монтированием. Эту проверку выполняет программа fsck (сокращенно от filesystem check — проверка файловой системы). Последнее число в каждой записи в файле fstab определяет порядок проверки файловых систем. В примере, приведенном выше, видно, что корневая файловая система проверяет­ся первой, вслед за ней проверяются файловые системы home и boot. Устройства с нулем в последнем поле не проверяются стандартными механизмами.

Программа fsck может не только проверить целостность, но и восстановить по­врежденные файловые системы с той или иной степенью успеха в зависимости от масштаба повреждений. В Unix-подобных системах восстановленные фрагменты файлов помещаются в каталог lost+found, находящийся в корне каждой файловой системы Проверить наш флеш-диск (который предварительно необходимо размонтиро­вать) можно с помощью следующей команды: По моему опыту, файловые системы повреждаются крайне редко, если нет ни­каких проблем с аппаратной частью, таких как выход из строя привода диска В большинстве файловых систем обнаруженные на этапе загрузки повреждения вызывают остановку системы с выводом предложения запустить fsck перед про­должением В культуре Unix слово «fsck» часто используется взамен распространенного ругатель­ства, в котором три буквы совпадают с буквами в имени команды. Это показательно — вы почти наверняка будете произносить упомянутое слово, оказавшись в ситуации, вынуждающей запустить fsck. Те из вас, кто пользуется компьютерами, настолько старыми, что они оборудова­ны приводами гибких дисков, также смогут управлять этими устройствами Под­готовка чистого гибкого диска выполняется в два этапа Сначала нужно выпол­нить низкоуровневое форматирование диска, а затем создать файловую систему Для форматирования в данном случае используется программа dformat, которой передается имя устройства привода гибких дисков (обычно Обратите внимание, что здесь использован тип файловой системы msdos, чтобы создать старую (и меньшую по размерам) таблицу размещения файлов После подготовки диска он монтируется как любые другие устройства Обычно на компьютерах мы работаем с данными, организованными в файлы, од­нако точно так же можно работать с данными в «низкоуровневой» форме. Если взглянуть на содержимое диска, можно увидеть, что оно состоит из множества «блоков» данных, которые операционная система интерпретирует как файлы и каталоги. Если бы мы умели интерпретировать диски как простые коллекции блоков данных, мы смогли бы выполнять множество полезных задач, таких как клонирование дисков Эту задачу решает программа dd Она копирует блоки данных из одного места в другое. По историческим причинам команда имеет уникальный синтаксис:

Представьте, что у вас есть два флеш-диска USB одинакового размера и вам нужно создать точную копию первого диска на втором Допустим, что после подключе­ния к компьютеру им назначаются имена устройств /dev/sdb и /dev/sdc соответ­ственно В этом случае скопировать содержимое первого диска на второй можно следующей командой: Как вариант, если к компьютеру подключено только первое устройство, можно скопировать его содержимое в обычный файл, который впоследствии использо­вать для восстановления или копирования.

Создание образа компакт-диска

Запись на компакт-диски (CD-R или CD-RW) выполняется в два этапа: сначала нужно создать файл образа ISO, являющийся точным образом файловой системы компакт-диска, а затем записать файл образа на носитель (то есть на сам компакт - диск) Чтобы создать ISO-образ имеющегося компакт-диска, необходимо с помощью dd прочитать все блоки с данными с этого компакт-диска и скопировать их в ло­кальный файл Например, допустим, что у нас есть компакт-диск с дистрибути­вом Ubuntu, и мы хотим создать файл ISO-образа, который потом можно будет использовать для создания нескольких копий Вставив компакт-диск в привод CD-ROM и определив имя устройства (пусть это будет /dev/cdrom), мы сможем создать файл ISO-образа следующим способом: dd if=/dev/cdrom of=ubuntu. isoЭтот прием также применим к дискам DVD с данными, но он не будет работать с аудиодисками, так как для хранения данных на них файловая система не ис­пользуется. Если вы хотите скопировать аудиодиск, обратитесь к команде cdrdao. В руководствах по созданию и записи оптических дисков, таких как CDROM и DVD, которых в избытке на просторах Интернета, часто можно встретить упоминание двух программ, mkisofs и cdrecord. Эти программы были некогда частью популярного пакета cdrtools, созданного Йоргом Шиллингом (Jorg Schilling). Летом 2006-го мистер Шиллинг изменил лицензию в части, касающейся пакета cdrtools, из-за чего она, по мнению многих в сообществе пользователей Linux, стала несовместимой с GNU GPL. Как результат, на основе cdrtools был создан альтернативный проект, включающий программы wodim и genisoimage взамен cdrecord и mkisofs соответственно. Создать файл ISO-образа, включающий содержимое некоего каталога, можно с помощью программы enisoimage. Для этого сначала создадим каталог со все­ми необходимыми файлами для включения в образ и затем командой genisoimage создадим файл образа Например, если предположить, что вы создали каталог ~/cd-rom-files и наполнили его файлами для записи на компакт-диск, следующая команда создаст файл образа с именем cd-rom. iso: Параметр - R требует добавить метаданные расширений Rock Ridge, позволяющих использовать длинные имена файлов и права доступа к файлам в стиле POSIX. Аналогично, параметр - J включает расширения Joliet, разрешающие использовать длинные имена файлов в Windows После подготовки файла образа его можно записать на оптический носитель Большинство команд, обсуждаемых ниже, применимы и для записи на носители CD-ROM и DVD Существует один трюк, позволяющий монтировать ISO-образы, хранящиеся на жестком диске, и работать с ними, как если бы это были оптические носители Па­раметр - o loop, добавленный в команду mount (вместе с обязательным параметром - t iso9660, определяющим тип файловой системы), позволяет смонтировать файл образа в дерево файловой системы, как если бы это было обычное устройство: В примере, приведенном выше, мы создали точку монтирования с именем /mnt/ iso_image и затем смонтировали в нее файл образа image. iso. После монтирования образа с ним можно работать как с настоящим диском CD-ROM или DVD. Не за­будьте размонтировать образ, когда он станет не нужен Перезаписываемые компакт-диски CD-RW нужно стирать, или очищать, перед повторным использованием. Для этого воспользуемся командой wodim, указав ей имя устройства пишущего привода компакт-дисков и тип очистки Программа wodim предлагает несколько типов очистки. Для минимальной (и самой быстрой) очистки следует указать тип fast:

Запись образа

Записать образ можно с помощью все той же программы wodim, указав ей имя устройства пишущего привода компакт-дисков и имя файла образа: wodim dev=/dev/cdrw image. iso Помимо имени устройства и файла образа программа wodim поддерживает массу дополнительных параметров Чаще других используются параметры - v (обеспе­чивает вывод подробной информации в ходе записи) и - dao (выполняет запись на диск в режиме disc-at-once — диск целиком) . Режим «диск целиком» следует ис­пользовать, если вы собираетесь воспроизводить диски в коммерческих целях. По умолчанию wodim использует режим track-at-once (по одной дорожке), который хорошо подходит для записи музыкальных треков Часто бывает полезно проверить целостность ISO-образа, загруженного из Интернета. В большинстве случаев распространители ISO-образов сопровождают их файлами с контрольными суммами. Контрольная сумма — это результат экзоти­ческих математических вычислений в виде числа, представляющего содержимое целевого файла Если содержимое файла образа изменится хотя бы в одном бите, его контрольная сумма будет отличаться от указанной распространителем Для вычисления контрольной суммы чаще всего используется программа md5sum, воз­вращающая уникальное шестнадцатеричное число: Загрузив образ, запустите md5sum для него и сравните результат работы md5sum со значением, указанным распространителем Помимо проверки целостности загруженного файла, программу md5sum можно использовать для проверки вновь записанного оптического носителя Для этого сначала вычислите контрольную сумму для файла образа, а затем — для носителя Вся хитрость проверки носителя заключается в том, чтобы ограничить вычисле­ния частью оптического носителя, содержащей образ. Для этого определите число 2048-байтных блоков в образе (запись на оптические носители всегда выполня­ется блоками по 2048 байт) и прочитайте с носителя ровно столько блоков Для некоторых типов носителей это не обязательно Например, компакт-диск, запи­санный в режиме disc-at-once, можно проверить так: md5sum /dev/cdrom Многие типы носителей, такие как DVD, требуют точного вычисления числа бло­ков. Следующий пример демонстрирует проверку целостности файла образа dvd - image. iso и диска в устройстве /dev/dvd привода DVD. Вам понятно, как работает эта команда? Когда дело доходит до сетевых возможностей, трудно представить что-то, что было бы невозможно для Linux Linux используется для создания всех видов се­тевых систем, программных компонентов и устройств, включая брандмауэры, маршрутизаторы, серверы имен, сетевые устройства хранения данных (Network - Attached Storage, NAS) и так далее и тому подобное. Насколько обширна тема сетей, настолько же обширна коллекция команд, ко­торые можно использовать для настройки и управления ими. Мы сосредоточим свое внимание лишь на тех из них, которые чаще всего используются на практике В число команд, выбранных для исследования в этой главе, входят команды, ис­пользуемые для мониторинга сетей и передачи файлов Дополнительно мы иссле­дуем программу ssh, используемую для входа в удаленные системы В этой главе рассматриваются следующие команды: Далее предполагается, что вы имеете некоторые базовые знания о сетях В нашем мире повсеместного распространения Интернета каждый пользователь компью­тера должен иметь представление о том, как действуют сети, хотя бы на элемен­тарном уровне Для полноценного использования этой главы вы должны знать следующие термины.

Исследование и мониторинг сети

Даже если вы не являетесь системным администратором, бывает полезно уметь оценивать производительность и функционирование сети Команда ping является самой простой сетевой командой. Она посылает специаль­ные сетевые пакеты IMCP ECHO_REQUEST указанному сетевому узлу. Боль­шинство сетевых устройств принимает эти пакеты и отвечает на них, — это позво­ляет проверить сетевые соединения После прерывания нажатием CTRL+C (в данном примере после шестого пакета) ping выводит результаты своей работы Если сеть функционирует должным об­разом, число потерянных пакетов (packet loss) будет составлять ноль процентов. Успешная работа ping может служить признаком того, что сетевые компоненты (интерфейсные карты, кабели, маршрутизаторы и шлюзы) находятся в рабочем состоянии Программа traceroute (в некоторых системах используется похожая на нее про­грамма tracepath) выводит список всех «переходов» (hops) на пути сетевого тра­фика между локальной системой и указанным узлом сети Например, увидеть, как выглядит маршрут к сайту Здесь можно видеть, что на пути между нашей тестовой системой находится 16 маршрутизаторов. Для маршрутизаторов, предостав­ляющих идентификационную информацию, выводятся имена хостов, IP-адреса и информация о производительности, которая включает три интервала времени, понадобившихся для передачи/подтверждения пакетов между локальной систе­мой и маршрутизатором Для маршрутизаторов, не предоставляющих идентифи­кационной информации (например, из-за особенностей настройки маршрутиза­тора, заторов в сети, действий брандмауэров и т д ), выводятся звездочки, как это можно видеть в строке, соответствующей второму переходу Программа netstat используется для исследования различных настроек сети и статистик С помощью множества параметров этой команды можно просматри­вать самые разные аспекты настройки сети С помощью параметра - ie, например, можно исследовать сетевые интерфейсы в Пример, приведенный выше, показывает, что наша тестовая система имеет два сетевых интерфейса. Первый, с именем eth0, — это интерфейс Ethernet; второй, с именем lo, — это петлевой интерфейс (loopback), виртуальный интерфейс, кото­рый система использует, чтобы разговаривать «сама с собой» Выполняя причинно-следственную диагностику, первое, на что следует обратить внимание, — наличие слова UP в начале четвертой строки для каждого интерфейса, указывающего, что сетевой интерфейс включен, и присутствие допустимого IP - адреса в поле inet addr во второй строке. Для систем, использующих протокол ди­намической настройки хостов (Dynamic Host Configuration Protocol, DHCP), на­личие допустимого IP-адреса в этом поле подтвердит нормальную работу DHCP. Использование параметра - r позволит получить таблицу маршрутизации ядра. По этой таблице можно судить, как настроена передача пакетов между сетями:

В этом простом примере представлена типичная таблица маршрутизации для кли­ентской машины, подключенной к локальной сети (Local Area Network, LAN), на­ходящейся за брандмауэром/маршрутизатором. В первой строке демонстрируется адрес назначения 192.168.1.0. IP-адреса, оканчивающиеся нулем, соответствуют целым сетям, а не отдельным узлам в них, поэтому такой адрес подразумевает: «лю­бой узел в локальной сети» . Следующее поле, Gateway (шлюз), определяет имя или IP-адрес шлюза (маршрутизатора) для выхода текущего узла в указанную сеть. Звездочка в этом поле указывает, что использовать шлюз не требуется В последней строке в качестве адреса назначения указано слово default (по умолча­нию) . Эта строка управляет трафиком, адресованным любым сетям, не перечислен­ным в таблице В данном примере видно, что роль шлюза выполняет маршрутизатор с адресом 192.168.1.1, который, по всей видимости, знает, что делать с трафиком. Программа netstat имеет множество параметров, из которых мы рассмотрели только пару Полный их список вы найдете на странице справочного руководства (man) для netstat

Передача файлов по сети

Что толку от сети, если не знать, как перемещать файлы через нее? Существует множество программ, перемещающих данные по сети Сейчас мы рассмотрим две из них, а еще несколько — в последующих разделах Одна из по-настоящему «классических» программ — ftp — получила свое имя от используемого ею протокола, протокола передачи файлов (File Transfer Protocol, FTP) Протокол FTP широко используется в Интернете для передачи файлов Он поддерживается большинством веб-браузеров, если не всеми, и вам часто будут встречаться идентификаторы URI, начинающиеся с префикса протоко­ла Программа ftp появилась задолго до веб-браузеров Она использовалась для об­мена данными с серверами FTP, компьютерами, хранящими файлы, которые мож­но выгружать и загружать по сети Протокол FTP (в своем первоначальном виде) небезопасен, потому что пересы­лает имена и пароли в открытом текстовом виде. То есть они не шифруются, и любой, кто способен перехватить сетевой трафик, сможет увидеть их. По этой причине практически все операции по протоколу FTP в Интернете выполняют­ся анонимными северами FTP Анонимный сервер позволяет любому желающему подключиться с учетной записью anonymous без пароля. В следующем примере показан типичный сеанс работы с программой ftp для загрузки ISO-образа с дистрибутивом Ubuntu из каталога /pub/cd_images/ Ubuntu-8.04 анонимного сервера FTP В приводится описание команд, вводившихся в ходе этого сеанса. Если в приглашении ftp> ввести команду help, программа выведет список под­держиваемых команд С помощью программы ftp можно выполнять множество обычных операций с файлами на сервере, правда, при наличии достаточных при­вилегий. Это не очень удобно, но выполнимо. ftp — не единственный клиент FTP командной строки. В действительности та­ких клиентов множество. Одним из лучших (и более популярным) считается lftp Александра Лукьянова (Alexander Lukyanov). Этот клиент действует почти так же, как традиционная программа ftp, но имеет множество дополнительных функ­ций, включая поддержку нескольких протоколов (в том числе и HTTP), возмож­ность автоматического восстановления прервавшейся загрузки, выполнение опе­раций в фоновом режиме, автодополнение путей по клавише Tab и многое другое. wget — еще одна популярная программа командной строки для загрузки файлов Ее удобно использовать для загрузки содержимого веб - и FTP-сайтов. С помо­щью wget можно загрузить один файл, несколько файлов и даже целый сайт На­пример, загрузить первую страницу сайта Большинство параметров, поддерживаемых программой wget, позволяет орга­низовать рекурсивную загрузку, загрузку файлов в фоновом режиме (позволяет выйти из системы без остановки загрузки) и догружать частично загруженные файлы Эти возможности хорошо описаны на странице справочного руководства (man) Уже много лет Unix-подобные операционные системы поддерживают возмож­ность удаленного администрирования по сети На первом этапе, еще до повсемест­ного распространения Интернета, существовала пара популярных программ для входа в удаленные сетевые узлы: rlogin и telnet Однако эти программы страдали тем же фатальным недостатком, что и программа ftp; все данные (включая имена пользователей и пароли) они передавали в виде открытого текста Это совершен­но недопустимо в эпоху Интернета

Ssh — безопасный вход в удаленные компьютеры

Для решения описанной проблемы был разработан протокол с названием SSH (Secure Shell — безопасная командная оболочка) SSH решает две основные про­блемы безопасного взаимодействия с удаленными сетевыми узлами. Во-первых, он подтверждает, что удаленный узел является именно тем, за кого себя выдает (это предотвращает атаки вида «злоумышленник в середине» (man-in-the-middle), и, во - вторых, шифрует все данные, передаваемые между локальным и удаленным узлами В своей работе протокол SSH опирается на два компонента. На удаленном узле действует сервер SSH, принимающий соединения на порте 22, а в локальной си­стеме действует клиент SSH, осуществляющий обмен информацией с удаленным сервером Большинство дистрибутивов Linux включают реализацию SSH с названием OpenSSH из проекта BSD. Некоторые дистрибутивы (например, Red Hat) по умол­чанию содержат пакеты с обоими компонентами, сервером и клиентом, тогда как другие (например, Ubuntu) включают только клиента Чтобы система могла при­нимать удаленные соединения, в ней должен быть установлен пакет с реализаци­ей сервера OpenSSH-server, этот сервер должен быть настроен и запущен, и если система находится за брандмауэром, последний должен пропускать входящие со­единения на порт TCP с номером 22 Программа клиента SSH, используемая для подключения к серверам SSH, имеет достаточно очевидное имя: ssh. Подключиться к удаленному сетевому узлу с име­нем remote-sys можно с помощью программы клиента ssh, как показано ниже: Are you sure you want to continue connecting (yes/no)? При первой попытке подключения на экран выводится предупреждение, сообща­ющее, что аутентичность удаленного узла не может быть установлена. Это объ­ясняется тем, что программа-клиент прежде никогда не подключалась к данному удаленному узлу. Чтобы принять идентификационные данные удаленного узла, введите yes в ответ на приглашение После установки соединения пользователю будет предложено ввести пароль: Сеанс с удаленной командной оболочкой продолжается, пока пользователь не введет команду exit в приглашении удаленной командной оболочки, после чего соединение закроется В этот момент возобновится сеанс локальной командной оболочки и появится ее приглашение к вводу К удаленной системе можно также подключиться с другим именем пользователя. Например, если локальный пользователь me имеет в удаленной системе учетную запись с именем bob, он сможет войти в удаленную систему с именем bob, выпол­нив следующую команду: Это сообщение появляется в двух возможных ситуациях Первая: злоумышлен­ник мог попытаться провести атаку вида «злоумышленник в середине» Это слу­чается редко, потому что все знают, что ssh предупреждает пользователя об этом. Более вероятная причина связана с некими изменениями в удаленной системе: например, была выполнена переустановка операционной системы или сервера SSH Однако в интересах безопасности не следует сбрасывать со счетов первую возможность Всегда обращайтесь к системному администратору удаленной си­стемы, когда появится это сообщение Убедившись в безобидности причин, вызвавших это сообщение, можно исправить проблему на стороне клиента Для этого с помощью текстового редактора (напри­мер, vim) удалите устаревший ключ из файла ~/.ssh/known_hosts. В примере со­общения выше присутствует строчка.

Поиск файлов

Блуждая по системе Linux, мы совершенно ясно увидели, что типичная Linux - система содержит множество файлов В связи с этим возникает вопрос: как ис­кать нужные файлы? Мы уже знаем, что файловая система в Linux организована в соответствии с определенными соглашениями, которые переходили из одного поколения Unix-подобных систем в другое, но огромное число файлов может по­рождать труднопреодолимую проблему

Дополнительно в этой главе будет представлена пара команд, которые помогут нам в наших исследованиях: Программа locate выполняет быстрый поиск в базе данных имен файлов и вы­водит все имена, соответствующие искомой строке Допустим, к примеру, что нужно найти все программы с именами, начинающимися с zip. Поскольку тре­буется найти программы, можно предположить, что имя каталога с программами оканчивается на bin/. Соответственно можно попробовать выполнить поиск с по­мощью locate, как показано ниже:

Если к результатам поиска предъявляются более строгие требования, команду locate можно объединить с другими инструментами, такими как grep, позволяю­щими осуществить более сложный поиск: Программа locate существует уже много лет, и за эти годы было создано несколько ее вариантов, получивших широкое распространение Два из них, наиболее часто используемые в современных дистрибутивах Linux, — это slocate и mlocate, ко­торые, впрочем, являются символическими ссылками, указывающими на locate Разные версии locate имеют пересекающиеся множества параметров. Некоторые поддерживают поиск с использованием регулярных выражений (о которых рас­сказывается в главе 19) и групповые символы. Несмотря на большое число проверок, поддерживаемых командой find, мы все еще нуждаемся в способе определения логических отношений между проверками. Например, представьте, что в некотором каталоге мы хотим найти все файлы и под­каталоги с небезопасными разрешениями Для этого можно было бы выполнить поиск всех файлов с разрешениями, отличающимися от 0600, и каталогов с раз­решениями, отличающимися от 0700 К счастью, find поддерживает возможность комбинирования проверок с помощью логических операторов, с целью опреде­лить более сложные критерии отбора Выразить вышеупомянутую проверку мож­но так: Ф-фу! Как неизящно! Что все это значит? На самом деле операторы перестанут казаться избыточно сложными, как только вы с ними познакомитесь поближе Имея список операторов под рукой, попробуем разобрать команду find На самом верхнем уровне мы видим, что проверки объединены в две группы, разделенные оператором-or: В этом есть определенный смысл, потому что мы хотим найти файлы с одним на­бором разрешений и каталоги - с другим Но если выполняется поиск и файлов и каталогов, почему используется оператор - or вместо - and? Потому что find, выполняя обход файлов и каталогов, оценивает их по одному, чтобы понять, соот­ветствует ли файл или каталог указанным проверкам Команде требуется узнать, является ли очередной элемент файлом или каталогом с «плохими» разрешения­ми. Один и тот же элемент не может соответствовать сразу двум условиям. То есть если развернуть сгруппированные выражения, можно увидеть следующее: (файл с плохими разрешениями) - or (каталог с плохими разрешениями) Наша следующая задача — проверить «плохие разрешения». Как это сделать? Фактически никак. Но мы можем проверить «неудовлетворительные разреше­ния», зная, что такое «удовлетворительные разрешения» . В случае с файлами удовлетворительными являются разрешения 0600, для каталогов — 0700. Выра­жение, проверяющее «неудовлетворительные» разрешения, выглядит так: оператор - and можно просто удалить, так как он под­разумевается по умолчанию. Теперь, объединив все вместе, мы получим оконча­тельную команду: Однако поскольку круглые скобки имеют специальное значение для командной обо­лочки, их нужно экранировать, чтобы предотвратить интерпретацию скобок команд­ной оболочкой. Для этого достаточно добавить обратный слеш перед каждой из них.

Действия в процессе поиска

Давайте попробуем выполнить определенные действия в процессе поиска! Иметь список с результатами работы команды find уже неплохо, но представьте, что нам нужно выполнить некие операции с элементами списка К счастью, find позволяет выполнять наши операции, основываясь на результатах поиска Существует множество предопределенных операций и несколько способов при­менения операций, определяемых пользователем Для начала взгляните на непол­ный список предопределенных операций Поддерживаемых операций намного больше, чем показано здесь Полный список можно найти на странице справочного руководства (man) для команды find Она выводит список всех файлов и подкаталогов, хранящихся в домашнем ката­логе Список выводится просто потому, что в отсутствие других операций предпо­лагается операция - print. То есть эту команду можно было бы выразить так: Программу find можно использовать для удаления файлов, соответствующих определенным критериям Например, следующая команда удалит все файлы с расширением. BAK (которое часто используется для обозначений резервных ко­пий файлов): Эта команда найдет в домашнем каталоге (и во вложенных подкаталогах) пользо­вателя все файлы с расширением. BAK и удалит их. Логические операторы имеют еще одну важную особенность, с которой необходи­мо разобраться Представьте, что у нас есть два выражения, разделенных логиче­ским оператором: Почему так происходит? Это сделано для повышения производительности Возь­мем для примера оператор - and. Мы знаем, что выражение выражение1 - and выражение2 не может быть истинным, если выражение1 вернет ложный результат, поэтому нет смысла вычислять выражение2 . Аналогично, если имеется выражение выражение1 - or выражение2 и выражение1 вернет истинный результат, нет смысла вычислять вы - ражение2, так как уже известно, что выражение1 - or выражение2 является истинным. Это удобно, поскольку такой порядок вычислений помогает повысить скорость выполнения Но почему это так важно для нас? Потому что мы можем использо­вать данную особенность для управления выполнением операций, о которых рас­сказывается далее Прежде чем продолжить, давайте посмотрим, как логические операторы воздей­ствуют на операции Взгляните на следующую команду: Как видите, эта команда ищет обычные файлы (-type f) с расширением. BAK (-name '*.BAK') и выводит относительные пути к ним в стандартный вывод (-print) . Однако такой порядок работы команды определяется логическими от­ношениями между всеми проверками и операциями Как вы помните, между про­верками и операциями по умолчанию подразумевается отношение - and. Ту же команду можно выразить, добавив логические операторы: Теперь, имея перед глазами это определение, взгляните где показано, как логические операторы влияют на порядок выполнения Так как логические отношения между проверками и операциями определяют необ­ходимость их выполнения, можно сделать вывод, что их порядок следования играет важную роль. Например, если изменить порядок выполнения операций и проверок, поставив операцию - print на первое место, команда будет вести себя иначе:

Эта версия команды выведет каждый файл (операция - print всегда возвращает истинное значение), а затем проверит тип файла и его расширение Далее воспользуемся командой find, чтобы обновить время последнего изменения некоторых файлов в нашей песочнице: Эта команда обновит время последнего изменения для всех файлов с именем file-B, имеющихся в песочнице. Теперь найдем с помощью find обновленные фай­лы, сравнив все файлы с эталонным файлом timestamp: В результате мы получим все 100 файлов с именем file-B. Поскольку команда touch применялась ко всем файлм file-B в песочнице после обновления файла timestamp, они оказались «новее», чем timestamp, и потому были идентифициро­ваны проверкой –newer В заключение вернемся к проверке плохих разрешений, выполнявшейся выше, и применим ее к каталогу playground: Эта команда выведет все 100 каталогов и 2600 файлов, хранящихся в playground (а также файл timestamp и сам каталог playground, всего 2702 элемента), потому что ни один из них не соответствует нашему определению «удовлетворительные разрешения» Вооружившись новыми знаниями об операторах и операциях, до­бавим в эту команду операции для применения новых разрешений к файлам и ка­талогам в песочнице: Основываясь на повседневном опыте, следует отметить, что намного проще вве­сти две команды — одну для каталогов и одну для файлов, чем одну большую со­ставную команду, но знание, что можно действовать именно так, вам не помешает Главное, что вы должны понять, — как можно использовать операторы и операции для решения практических задач Наконец мы добрались до параметров Параметры помогают управлять областью поиска Они могут включаться в выражения команды find наряду с другими про­верками и операциями.

Архивация и резервное копирование

Одной из основных задач администратора компьютерных систем является обе­спечение безопасности данных, а одним из способов решения этой задачи — свое­временное создание резервных копий системных файлов Даже если вы не явля­етесь системным администратором, вам все равно пригодится умение создавать копии и перемещать большие коллекции файлов из одного места в другое и с од­ного устройства на другое

В этой главе мы рассмотрим несколько программ, часто используемых для управ­ления коллекциями файлов, в том числе: На протяжении всей истории развития вычислительных технологий не прекра­щались попытки размещения большего числа данных в меньшем объеме, будь то память, устройства хранения или полоса пропускания сети Многие устройства и технологии, прочно вошедшие в обиход, такие как переносные плееры, телеви­дение высокой четкости или широкополосный доступ в Интернет, обязаны своим существованием эффективным технологиям сжатия данных. Сжатие данных — это процесс устранения избыточных данных. Давайте рассмот­рим воображаемый пример. Допустим, у нас есть файл, хранящий изображение абсолютно черного квадрата размером 100 на 100 пикселей. В терминах хране­ния данных (если предположить, что каждый пиксель представлен 24 битами, или 3 байтами) изображение занимает 30 000 байт: 100 X 100 X 3 = 30 000. Изображение, состоящее из пикселей одного цвета, содержит массу избыточных данных. Будь мы умнее, мы могли бы закодировать данные в виде простого описа­ния того факта, что изображение представлено блоком из 30 000 пикселей черного цвета. То есть вместо хранения блока данных с 30 000 нулей (черный цвет в фай­лах изображений обычно представлен нулевым значением) мы могли бы сжать данные до числа 30 000 с последующим нулем, описывающим цвет Такая схема сжатия, она называется кодированием длин серий (run-length encoding), является одной из простейших технологий сжатия. Современные технологии не в пример сложнее и эффективнее, но главная цель осталась прежней — избавиться от из­быточных данных Алгоритмы сжатия (математические методики, применяемые для осуществления сжатия) делятся на две основные категории: без потерь (lossless) и с потерями (lossy) . Сжатие без потерь гарантирует сохранность всех данных, содержащихся в оригинале. То есть после восстановления файла из сжатой версии восстанов­ленный файл будет иметь в точности то же содержимое, что и несжатый ориги­нал Сжатие с потерями, с другой стороны, удаляет некоторые данные во время сжатия, чтобы обеспечить более высокую степень сжатия Восстановленный файл в этом случае не будет совпадать с оригинальной версией, скорее он будет близкой аппроксимацией оригинала. Примерами сжатия с потерями могут служить фор­мат JPEG (для изображений) и MP3 (для музыкальных произведений) . В даль­нейшем обсуждении мы будем рассматривать только сжатие без потерь, посколь­ку большинство данных в компьютерах потерь не допускает

Qzip — сжатие и распаковывание файлов

Программа gzip используется для сжатия одного или нескольких файлов. Во вре­мя работы она замещает оригинальный файл его сжатой версией Соответствую­щая программа gunzip используется для восстановления сжатых файлов до ис­ходного состояния. Например: В этом примере мы создали текстовый файл с именем foo. txt, записав в него список содержимого каталога /etc. Далее мы запустили программу gzip, которая замени­ла оригинальный файл сжатой версией с именем foo. txt. gz. В списке содержимого каталога, который был получен с использованием шаблона foo.*, можно видеть, что исходный файл действительно был замещен сжатой версией, и эта сжатая вер­сия получилась почти в пять раз меньше оригинала. Можно также заметить, что сжатый файл имеет такие же разрешения и время, что и оригинал Далее мы запустили программу gunzip, чтобы распаковать файл. После этого, как видите, сжатая версия была замещена оригиналом, и снова с теми же разрешени­ями и временем Здесь мы заменили файл foo. txt его сжатой версией с именем foo. txt. gz. Затем проверили целостность сжатой версии, передав параметры - t и - v. В заключение мы распаковали файл, вернув его исходное состояние gzip можно также использовать несколько необычным способом, через стандарт­ные ввод и вывод: Эта команда создает сжатую версию списка с содержимым каталога. Программа gunzip, которая распаковывает файлы, сжатые с помощью gzip, пред­полагает, что имена файлов оканчиваются расширением. gz, поэтому его можно не указывать при условии, что имя файла в команде не соответствует существующе­му несжатому файлу: Если цель только в том, чтобы просмотреть содержимое сжатого текстового фай­ла, сделать это можно так: Вместе с gzip обычно поставляется программа zcat, которая действует подобно программе gunzip с параметром - c. Она применяется к файлам, сжатым с помо­щью gzip, по аналогии с командой cat: Программа bzip2 Джулиана Сюарда похожа на программу gzip, но использует иной алгоритм, который обеспечивает более высокую степень сжатия ценой сни­жения скорости работы Во многих отношениях она действует точно так же, как gzip. Файл, сжатый с помощью bzip2, получает расширение. bz2: Как видите, bzip2 можно использовать так же, как gzip. Все параметры програм­мы gzip (кроме - r), представленные выше, поддерживаются также программой bzip2. Но имейте в виду, что параметр степени сжатия (-число) имеет несколько иной смысл для bzip2. В паре с bzip2 поставляются программы bunzip2 и bzcat для распаковывания файлов Существует также программа bzip2recover для восстановления поврежденных файлов формата НЕ Мне иногда приходится видеть, как кто-то пытается сжать файл, уже сжатый с при­менением эффективного алгоритма сжатия, выполняя нечто подобное: Это напрасная трата времени и дискового пространства! Если применить процедуру сжатия к уже сжатому файлу, зачастую получается файл большего размера. Это объясняется тем, что все методики сжатия добавляют в файл некоторую служебную информацию, описывающую сжатие. Если попытаться сжать файл, не содержащий избыточной информации, сжатие не приведет к экономии места, которая могла бы покрыть расходы на хранение служебной информации.

Архивирование файлов

Часто вместе со сжатием используется операция архивирования. Архивирова­ние — это процесс сбора множества файлов и упаковывание их в один большой файл Архивирование часто применяется как один из этапов создания резервных копий системы. Оно также используется при перемещении старых данных из си­стемы в некоторое долговременное хранилище В мире программного обеспечения для Unix-подобных систем существует программа tar — классический инструмент для архивирования файлов. Ее имя, ко­торое расшифровывается как tape archive (архив на магнитной ленте), указывает, что первоначально инструмент предназначался для создания архивов на магнит­ных лентах Он до сих пор используется для решения этой традиционной задачи, но с неменьшим успехом поддерживает другие устройства хранения Нам часто приходится видеть имена файлов с расширением. tar или. tgz, которые обознача­ют «простые» tar-архивы и архивы, сжатые с помощью gzip соответственно. Ар­хив может состоять из группы отдельных файлов, иерархий каталогов или и того и другого. Команда tar имеет следующий синтаксис: где под режимом подразумевается один из нескольких режимов работы, перечис­ленных (здесь представлены не все параметры; полный список вы най­дете на странице справочного руководства (man) для tar) В программе tar используется немного непривычный способ определения пара­метров, поэтому рассмотрим несколько примеров ее использования Для начала воссоздадим нашу песочницу, как мы это делали в предыдущей главе: Эта команда создаст tar-архив с именем playground. tar, включающий всю иерар­хию каталогов песочницы. Как видите, режим и параметр f, который использует­ся для определения имени tar-архива, можно объединять, и при этом не требуется использовать начальный дефис. Но имейте в виду, что режим всегда должен ука­зываться первым, перед любыми параметрами Посмотреть содержимое архива можно с помощью следующей команды: Теперь извлечем содержимое архива в другой каталог. Для этого создадим новый каталог с именем foo, перейдем в него и извлечем содержимое tar-архива: Если внимательно исследовать содержимое ~/foo/playground, можно заметить, что в результате распаковывания архива мы получили точные копии оригинальных файлов Однако следует помнить, что если вы не действуете от имени суперпользо­вателя, файлы и каталоги, извлеченные из архива, будут принадлежать пользовате­лю, выполнившему восстановление, а не первоначальному их владельцу Другой интересной особенностью tar является способ обработки путей в архивах. По умолчанию используются относительные пути, а не абсолютные Для этого программа tar просто удаляет начальный слеш во всех путях Чтобы показать это, создадим снова наш архив, но на этот раз укажем абсолютный путь к архивируе­мому каталогу: Как вы помните, командная оболочка заменит ~/playground полным путем /home/ me/playground после нажатия клавиши ENTER, благодаря этому мы получим пол­ный путь для нашей демонстрации Далее извлечем архив, так же как прежде, и посмотрим, что из этого получилось: Как видите, здесь при извлечении архива каталог home/me/playground был воссоздан не в корневом, а в текущем рабочем каталоге ~/foo, как было бы в случае с абсолютными путями. Это может показаться странным, но такое решение имеет свои преимущества: оно позволяет извлекать архивы в любое другое место, а не только в исходное Повторив это упражнение с параметром, управляющим выво­дом дополнительных сообщений (v), можно получить более понятную картину происходящего

Пример использования tar

Рассмотрим пусть и гипотетический, но все же имеющий практическую ценность пример использования tar Представим, что нужно скопировать домашний ка­талог со всем его содержимым в другую систему и у нас имеется жесткий диск, подключаемый к порту USB, который можно использовать для переноса файлов. В современных системах Linux такие диски «как по волшебству» автоматически монтируются в каталог /media Допустим также, что подключаемый жесткий диск имеет том с именем BigDisk. Чтобы создать требуемый архив, выполним следую­щую команду: После записи файла следует отмонтировать диск и подключить его ко второму компьютеру. И снова он автоматически монтируется в каталог /media/BigDisk. Чтобы извлечь архив, выполните следующие команды: Обратите внимание, что здесь сначала выполняется переход в каталог /, чтобы извлечение производилось относительно корневого каталога, потому что все пути в архиве — относительные

При распаковке архива можно ограничить количество извлекаемых данных На­пример, можно извлечь из архива единственный файл: Добавление в конец команды пути к файлу гарантирует извлечение только этого файла. Можно указать несколько путей. Обратите внимание, что путь к файлу дол­жен быть полным относительным путем в архиве Обычно в путях к файлам нельзя использовать групповые символы; но GNU-версия tar (именно эта версия входит в состав большинства дистрибутивов Linux) поддерживает параметр - - wildcards. В следующем примере используется файл playground2.tar, созданный выше: Эта команда извлечет только файлы, соответствующие указанному пути с груп­повым символом Программа tar часто используется в сочетании с find. В следующем примере команда find используется для поиска файлов, подлежащих включению в архив: Здесь команда find отыскивает в каталоге playground все файлы с именем file-A и затем с помощью операции - exec вызывает tar в режиме добавления в конец (r), чтобы добавить найденные файлы в архив playground. tar. Использование tar в сочетании с find предоставляет отличный способ инкремент­ного резервного копирования дерева каталогов или всей системы. Применяя find для поиска файлов, более новых, чем эталонный файл, определяющий отметку времени, можно создать архив, содержащий только более новые файлы, чем фай­лы предыдущего архива, при этом предполагается, что время последнего измене­ния эталонного файла будет изменяться сразу после создания архива. Программа tar способна также использовать стандартный ввод и стандартный вывод Например: Здесь программа find создает список файлов и передает его по конвейеру програм­ме tar. Когда программе tar передается имя файла - (дефис), под ним подразу­мевается стандартный ввод или стандартный вывод, в зависимости от контекста (Кстати, соглашение об использовании дефиса (-) для представления стандартно­го ввода/вывода используется также многими другими программами. ) Параметр --files-from (который можно заменить эквивалентным параметром - T) заставля­ет tar читать список путей из файла, а не из командной строки.

Архив, произведенный программой tar

Архив, произведенный программой tar, передается по конвейеру программе gzip, что­бы в результате получить сжатый архив playground. tgz. Расширение. tgz по обще­принятому соглашению используется для tar-архивов, сжатых программой gzip. В некоторых случаях используется расширение. tar. gz. В примере, приведенном выше, для сжатия архива использовалась внешняя про­грамма gzip, однако современные GNU-версии tar поддерживают возможность gzip - и bzip2-сжатия своими встроенными средствами, для чего служат параметры z и j соответственно. Взяв за основу предыдущий пример, его можно упростить, как показано ниже: Произведя простую замену параметра сжатия z на j (и изменив расширение вы­ходного файла на. tbz, указывающее, что для сжатия использовался алгоритм bzip2), мы задействовали bzip2-сжатие. Другой интересный пример использования поддержки стандартного ввода и выво­да командой tar связан с передачей файлов между системами по сети Представьте, что имеется две машины, действующие под управлением Unix-подобных систем и имеющие программы tar и ssh. В этом случае можно организовать передачу ка­талога из удаленной системы (с именем remote-sys в этом примере) в локальную: Здесь мы скопировали каталог Documents из удаленной системы remote-sys в ка­талог с именем remote-stuff в локальной системе. Как это получилось? Во-первых, мы запустили программу tar в удаленной системе с помощью команды ssh. Как вы наверняка помните, ssh позволяет выполнить программу на удаленном ком­пьютере в сети и «увидеть» результат в локальной системе — стандартный вывод, полученный в удаленной системе, пересылается в локальную систему для обзора Мы воспользовались этой особенностью и заставили tar создать архив (режим c) и вывести его не в файл, а в стандартный вывод (параметр f с дефисом в качестве аргумента), вследствие чего архив передается через шифрованный туннель, соз­данный программой ssh, локальной системе. В локальной системе мы вызвали tar с целью распаковать архив (режим x), полученный со стандартного ввода (все тот же параметр f с дефисом в качестве аргумента) Программа zip одновременно является и инструментом сжатия, и архиватором. Формат файлов, используемый программой, знаком пользователям Windows — программа читает и создает файлы с расширением. zip. Однако в Linux чаще дру­гих используется программа сжатия gzip, а второе место занимает bzip2 . Поль­зователи Linux используют zip в основном для обмена файлами с системами Windows, а не как основной инструмент сжатия и архивирования Без параметра - r (отвечает за рекурсивный обход каталогов) в архив будет вклю­чен только каталог playground (без своего содержимого) . Расширение. zip добав­ляется к имени выходного файла автоматически, а мы включили его в пример для наглядности В процессе создания zip-архива программа zip обычно выводит последователь­ность сообщений, как показано ниже: сообщения показывают состояние каждого файла, добавленного в архив zip добавляет файлы в архив, используя один из двух методов: либо «store» (простое сохранение) — без сжатия, как в примере, приведенном выше, либо «deflate» — со сжатием Числовое значение, следующее за названием метода добавления, указывает достигнутую степень сжатия Поскольку в нашей песочнице хранятся только пустые файлы, сжатие их содержимого не производится Извлечение содержимого из zip-архива выполняется просто — с помощью про­граммы unzip: Одно важное отличие zip (от tar) состоит в том, что если указанный архив суще­ствует, он дополняется, а не замещается То есть существующий архив сохраняет­ся, новые файлы добавляются в него, а существующие — замещаются. Программа unzip позволяет выводить информацию о файлах и выборочно извле­кать их, достаточно только передать ей имя интересующего нас файла: При наличии параметра - l программа unzip просто выведет информацию о содер­жимом архива, не извлекая файл Если имя файла (или файлов) не указано, unzip выведет список всех файлов в архиве Для получения более подробной информа­ции следует добавить параметр - v Обратите внимание, что когда при извлечении из архива обнаруживается конфликт с существующим файлом, перед его заменой у пользователя запрашивается разрешение Подобно программе tar zip может использовать стандартный ввод и вывод, хотя реализация этой возможности имеет меньшую практическую ценность С помо­щью параметра -@ программе zip по конвейеру передается список имен файлов:

Синхронизация файлов и каталогов

В задачах резервного копирования систем широко используется стратегия син­хронизации одного или нескольких каталогов с другими каталогами, находящи­мися в локальной системе (обычно на некотором извлекаемом устройстве) или в удаленной Можно, к примеру, создать локальную копию веб-сайта, находяще­гося в разработке, и синхронизировать ее время от времени с «рабочей» копией на удаленном веб-сервере В мире Unix-подобных систем для решения этой задачи широко используется ин­струмент rsync. Эта программа синхронизует локальные и удаленные каталоги, используя протокол rsync remote-update (протокол удаленного обновления rsync), который позволяет rsync быстро обнаруживать различия между двумя каталога­ми и копировать минимальный объем данных, необходимый для синхронизации Это делает программу rsync быстрой и экономичной по сравнению с другими про­граммами копирования Программа rsync имеет следующий синтаксис: rsync параметры источник приемник где роль источника и приемника могут играть: Обратите внимание, что либо источник, либо приемник должен находиться в ло­кальной системе Копирование из удаленной системы в удаленную систему не поддерживается Давайте попробуем синхронизировать несколько локальных файлов Сначала очистим наш каталог foo: Далее синхронизируем каталог playground с соответствующей копией в foo: [me@linuxbox ~]$ rsync - av playground foo Мы добавили два параметра: - a (для архивирования — обеспечивает рекурсивный обход и сохранение атрибутов файлов) и - v (подробный вывод), чтобы отразить каталог playground в каталог foo. В процессе выполнения команды можно просма­тривать список копируемых файлов и каталогов В конце программа выведет ито­говое сообщение, как показано ниже, включающее общий объем скопированных данных: Если теперь запустить команду еще раз, результат будет другой: Обратите внимание на отсутствие списка файлов Это объясняется тем, что про­грамма rsync не обнаружила различий между ~/playground и ~/foo/playground и поэтому ничего не скопировала Если теперь изменить файл в playground и запу­стить rsync еще раз, она обнаружит изменившийся файл и скопирует только его В качестве примера представьте воображаемый внешний жесткий диск, использо­вавшийся выше с командой tar. Если после подключения такого диска к системе он снова будет смонтирован в каталог /media/BigDisk, выполним первое резервное копирование системы, для начала создав каталог /backup на внешнем устройстве, а затем вызвав rsync для копирования наиболее важных компонентов системы на внешнее устройство: В этом примере мы скопировали каталоги /etc, /home и /usr/local из нашей си­стемы на воображаемый внешний диск. Мы добавили параметр --delete, чтобы удалить файлы, которые могут присутствовать на устройстве с резервной копией, но отсутствовать на устройстве-источнике (этот параметр не нужен при создании резервной копии в первый раз, но является полезным дополнением в последую­щих операциях копирования) Периодическое повторение процедуры подключе­ния внешнего диска и запуск этой команды rsync является неплохим (хотя и не идеальным) способом сохранения резервной копии небольшой системы Конечно, здесь также могло бы пригодиться создание псевдонима Определим псевдоним и добавим его в свой файл. bashrc, чтобы обеспечить возможность быстрого ре­зервного копирования: alias backup='sudo rsync - av --delete /etc /home /usr/local /media/BigDisk/ backup' Теперь, чтобы выполнить всю работу, достаточно просто подключить внешний диск и ввести команду backup.

Использование rsync для копирования по сети

Одно из самых больших достоинств rsync — возможность копирования файлов по сети, об этом нам «говорит» буква r в названии rsync, что означает remote (уда­ленная) . Удаленную синхронизацию можно выполнить одним из двух способов. Первый можно использовать с удаленными системами, где установлена rsync и программа удаленной командной оболочки, такая как ssh. Допустим, что в ло­кальной сети имеется другая система с огромным объемом дискового простран­ства и мы хотели бы использовать эту систему для хранения резервной копии вместо внешнего диска Если допустить, что в этой системе уже имеется каталог /backup, куда можно было бы сохранить наши файлы, мы могли бы выполнить резервное копирование так: Мы внесли два изменения в команду, чтобы обеспечить копирование по сети Во - первых, добавили параметр --rsh=ssh, который требует от rsync использовать в качестве удаленной командной оболочки программу ssh. Благодаря этому для передачи данных из локальной системы в удаленную мы можем использовать шифрованный туннель SSH. Во-вторых, мы добавили имя удаленного узла (в дан­ном примере remote-sys) перед именем удаленного каталога. Второй способ использования rsync для синхронизации файлов по сети заклю­чается в использовании сервера rysnc. rsync можно настроить на работу в режи­ме демона, принимающего входящие запросы на синхронизацию Этот прием часто используется для зеркалирования удаленных систем Например, компания Red Hat Software поддерживает огромный репозиторий программных пакетов, разрабатываемых для ее дистрибутива Fedora. Для специалистов, занимающих­ся тестированием программного обеспечения, очень удобно иметь зеркало этой коллекции в ходе этапа тестирования, предшествующего этапу выпуска дистрибу­тива Поскольку файлы в репозитории обновляются достаточно часто (порой по нескольку раз в день), неплохо было бы организовать периодическую синхрониза­цию локального зеркала вместо копирования всего объема репозитория Один из таких репозиториев хранится в университете Georgia Tech; мы могли бы создать его зеркало с помощью локальной программы rsync и сервера rsync в Georgia Tech: core/development/i386/os fedora-devel В этом примере мы использовали идентификатор URI удаленного сервера rsync, включающий протокол (rsync://), имя удаленного узла (rsync. gtlib. gatech. edu) и путь к репозиторию В следующих нескольких главах мы познакомимся с инструментами для рабо­ты с текстом Как вы уже знаете, текстовые данные играют важную роль в Unix - подобных системах, таких как Linux Но прежде чем переходить к исследованию возможностей этих инструментов, необходимо познакомится с технологией, кото­рая часто ассоциируется с самыми сложными случаями использования этих ин­струментов — регулярными выражениями Знакомясь со свойствами и особенностями командной строки, мы уже встреча­ли некоторые по-настоящему таинственные свойства и команды, такие как меха­низмы подстановки и экранирования, короткие комбинации клавиш и история команд, не говоря уже о редакторе vi. Регулярные выражения продолжают этот список и являются (пожалуй) самым загадочным из всех инструментов Это не означает, что время на их изучение будет потрачено впустую Как раз наоборот Хорошее понимание регулярных выражений позволит вам творить настоящие чудеса, хотя истинная их ценность поначалу может быть и не очевидна

Что такое регулярные выражения?

Регулярные выражения — это всего лишь символическая форма записи, исполь­зуемая для идентификации шаблонов в тексте Они, до определенной степени, напоминают групповые символы, используемые командной оболочкой для выбо­ра соответствующих файлов и путей, но в более широком масштабе Регулярные выражения поддерживаются многими инструментами командной строки и боль­шинством языков программирования, чтобы упростить решение задач, связанных с обработкой текста. Однако проблема в том, что не все регулярные выражения одинаковы; разные инструменты и языки программирования используют соб­ственные «диалекты» регулярных выражений Для целей нашего обсуждения мы ограничимся регулярными выражениями, как они определены в стандарте POSIX (и поддерживаются большинством инструментов командной строки) в противо­положность многим языкам программирования (особенно это относится к Perl), где используются более широкие и богатые формы записи. При работе с регулярными выражениями мы в основном будем использовать нашу старую добрую приятельницу — программу grep. Название grep в действи­тельности произошло от фразы «global regular expression print» (глобальный по­иск с помощью регулярного выражения и вывод), то есть, как видите, grep имеет некоторое отношение к регулярным выражениям. В сущности, grep просматрива­ет текстовые файлы в поисках совпадений с указанным регулярным выражением и выводит в стандартный вывод все строки с такими совпадениями. До сих пор мы передавали программе grep фиксированные строки, например: Программа grep имеет следующий синтаксис: grep [параметры] регулярное_выражение [файл...] Давайте создадим несколько текстовых файлов, чтобы наше исследование grep стало более предметным: В этом примере grep просматривает все перечисленные файлы в поисках строки bzip и находит два совпадения, оба в файле dirlist-bin. txt. Если бы нам достаточно было получить только имена файлов с совпадениями, а не сами совпадения, мы могли бы добавить параметр Несмотря на то что пока это не очевидно, во всех своих попытках поиска с помо­щью grep мы использовали регулярные выражения, хотя и очень простые Регу­лярное выражение bzip, к примеру, означает, что ему соответствуют только строки в файлах, содержащие не менее четырех символов и среди этих символов присут­ствуют символы b, z, i и p, следующие именно в таком порядке и между ними от­сутствуют какие-либо другие символы. Символы в строке bzip — это литеральные символы, то есть они соответствуют сами себе. Помимо литералов регулярные вы­ражения могут содержать метасимволы, они используются для определения бо­лее сложных критериев сопоставления К метасимволам регулярных выражений относятся следующие символы: Здесь выполнен поиск в наших файлах совпадений с регулярным выражением. zip. В полученных результатах имеется пара важных моментов, которые необходимо от­метить. Обратите внимание, что программа zip не была найдена. Это объясняется включением в регулярное выражение метасимвола точки, увеличившим длину обя­зательного совпадения до четырех символов; так как в имени программы zip всего три символа, оно не было найдено. Кроме того, если бы в наших списках имелись имена файлов с расширением. zip, они также были бы найдены, потому что символ точки в расширении файла интерпретировался бы как «любой символ». Символ крышки (л) и знак доллара ($) в регулярных выражениях интерпрети­руются как якоря. Это означает, что в их присутствии совпадение с регулярным выражением возможно, только если совпадение будет найдено в начале строки (л) или в ее конце ($). Здесь выполняется поиск в списке файлов строки zip, находящейся в начале стро­ки, в конце строки и занимающей всю строку, от начала до конца Обратите вни­мание, что регулярное выражение л$ (начало и конец без каких-либо символов между ними) будет соответствовать пустым строкам Моя супруга обожает разгадывать кроссворды и иногда просит меня помочь с ответом на какой-нибудь вопрос. Например: «Слово из пяти букв, третья j, последняя r, которое означает...» Подобные вопросы навели меня на размышления. Знаете ли вы, что в вашей системе Linux имеется словарь? Загляните в каталог /usr/ share/dict, и вы обнаружите там один или несколько словарей. Файлы словарей, находящиеся в каталоге, — это обычные длинные списки слов, по одному в стро­ке, упорядоченные по алфавиту. В моей системе файл words содержит больше 98 500 слов. Найти возможные ответы на вопрос в кроссворде можно с помощью следующей команды:

Это регулярное выражение помогает найти в файле словаря все слова, длиной в пять букв, где третья буква — j и последняя — r.

Выражения в квадратных скобках и классы символов

В дополнение к возможности описать в регулярном выражении совпадение с лю­бым символом в заданной позиции с помощью выражения в квадратных скобках можно также описать совпадение с одним символом из определенного множе­ства. Выражение в квадратных скобках помогает определить множество символов (включая символы, которые иначе интерпретировались бы как метасимволы), ко­торые находятся в данной позиции В следующем примере используется множе­ство из двух символов, благодаря которому обнаруживаются соответствия с по­следовательностями bzip и gzip: Множество может содержать любое число символов. Метасимволы, заключенные в квадратные скобки, теряют свое специальное значение. Лишь два метасимво­ла интерпретируются особым образом, но при этом они имеют иной смысл. Пер­вый — символ крышки (л), который используется для обозначения отрицания; второй — дефис (-), который используется для обозначения диапазона символов Если сразу после открывающей квадратной скобки стоит символ крышки (л), остальные символы множества интерпретируются как недопустимые в данной позиции Проверим это, изменив предыдущий пример: Включив отрицание, мы получили список файлов, имена которых содержат по­следовательность zip, которой предшествует любой символ, кроме b или g Об­ратите внимание, что файл zip не был найден. Символ отрицания не отменяет не­обходимости присутствия символа в заданной позиции, он лишь требует, чтобы символ в этой позиции не принадлежал указанному множеству Символ крышки обозначает операцию отрицания, только если является первым символом в выражении в квадратных скобках; в противном случае он теряет свое специальное значение и превращается в обычный символ Если необходимо сконструировать регулярное выражение, которое находило бы в наших списках все файлы с именами, начинающимися с заглавной буквы, это можно выполнить следующим образом: Достаточно просто поместить 26 букв в верхнем регистре в выражение в квадрат­ных скобках. Но необходимость ввода всех этих символов вызывает некоторое беспокойство, поэтому предусмотрен другой способ: Мы сократили множество с 26 буквами до 3-символьного диапазона. Так можно выразить любой диапазон символов и даже несколько диапазонов, например, для поиска имен файлов, начинающихся с буквы или цифры: Как следует из примеров, символ дефиса получает в диапазонах специальное зна­чение, поэтому возникает вопрос: как включить дефис в выражение в квадратных скобках, чтобы он интерпретировался как обычный символ? Для этого достаточно поставить его в начало выражения Например: Эта команда найдет все имена файлов, содержащие буквы верхнего регистра С другой Традиционные диапазоны символов — простой и эффективный способ опреде­ления наборов символов К сожалению, они могут использоваться не со всеми программами Мы не испытывали никаких проблем с диапазонами, используя программу grep, но могли бы столкнуться с ними при использовании других про­грамм (В разных дистрибутивах будут получены разные списки файлов, а в некоторых даже пустой список. Эти результаты получены в Ubuntu. ) Эта команда вернула ожидаемый результат — список имен файлов, начинающихся с заглавной буквы Но следующая команда даст совершенно другой результат (здесь приведена толь­ко часть результатов): В чем же причина? Для этой длинной истории имеется короткая версия. Во вре­мена, когда операционная система Unix только появилась на свет, был известен только один набор символов — ASCII, и этот факт нашел свое отражение в данной особенности. В ASCII первые 32 символа (с номерами 0-31) — это управляющие символы (такие, как табуляция, забой и возврат каретки). Следующие 32 (32-63) представляют печатаемые символы, включая большинство знаков пунктуации и цифры с нуля до девяти. Следующие 32 (с номерами 64-95) представляют бук­вы верхнего регистра и несколько знаков пунктуации Последние 31 (с номера­ми 96-127) представляют буквы нижнего регистра и еще несколько знаков пун­ктуации Опираясь на эту классификацию, системы, использующие набор ASCII, придерживались следующего порядка сопоставления: С ростом популярности Unix за пределами США возникла необходимость в под­держке символов, не входящих в алфавит американского английского Таблица ASCII была расширена до использования 8-битных символов, и в нее добавились символы с номерами 128-255, используемые во многих других языках. Для под­держки этой возможности в стандарт POSIX было введено понятие региона (locale), определяющее выбор набора символов для конкретного географического региона Узнать, какой язык настроен в вашей системе, можно с помощью команды

Чередование или выражение выбора

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

Для демонстрации воспользуемся комбинацией команд grep и echo Сначала по­пробуем выполнить простое сопоставление строк: При проверке этой настройки POSIX-совместимые приложения используют лек­сикографический порядок, а не порядок следования символов в наборе ASCII Это объясняет поведение команд, рассмотренное выше. Когда диапазон символов [A-Z] интерпретируется в лексикографическом порядке, он включает все алфа­витные символы, кроме символа a в нижнем регистре, — именно это объясняет полученный результат Для частичного решения этой проблемы стандарт POSIX предусматривает не­сколько классов символов, описывающих диапазоны символов. Они перечислены Достаточно простой пример, в котором мы передаем по конвейеру вывод команды echo на ввод grep и видим результат. Если обнаруживается совпадение, мы видим вывод; если совпадение отсутствует, ничего не выводится Теперь добавим чередование, обозначаемое метасимволом вертикальной черты:Здесь мы видим регулярное выражение AAA|BBB, которое означает «совпадение со строкой AAA или со строкой BBB». Так как это расширенная особенность, мы до­бавили в команду grep параметр - E (вместо этого можно было бы использовать программу egrep) и заключили регулярное выражение в кавычки, чтобы предот­вратить интерпретацию командной оболочкой символа вертикальной черты как оператора конвейера В чередовании может быть более двух вариантов: Для объединения с другими элементами регулярного выражения чередование можно заключать в круглые скобки () : Этому выражению будут соответствовать имена файлов из наших списков, начинающиеся с bz, gz или zip Если отбросить круглые скобки, смысл регулярного выражения изменится, и ему будут соответствовать имена, начинающиеся с bz или содержащие gz или zip: Расширенные регулярные выражения поддерживают несколько способов опреде­ления числа совпадений с элементом Этот квантификатор фактически означает: «совпадение с предыдущим элементом не обязательно» Представьте, что нужно проверить допустимость номера телефо­на, и предполагается, что номер допустим, если представлен в одной из двух форм: (nnn) nnn-nnnn или nnn nnn-nnnn, где n — это цифра. Для проверки можно было бы использовать следующее регулярное выражение: В этом выражении за круглыми скобками следуют знаки вопроса, указывающие, что скобки могут либо отсутствовать, либо присутствовать один раз. И снова, по­скольку круглые скобки считаются метасимволами (в ERE), мы экранировали их обратными слешами, чтобы они интерпретировались как литералы Попробуем применить это выражение:

Здесь регулярному выражению соответствуют обе формы записи номера телефо­на, но ему не соответствует номер, содержащий нецифровые символы Подобно метасимволу?, звездочка (*) обозначает необязательный элемент; однако, в отличие от знака вопроса (?), этот элемент может встречаться любое число раз, а не только единожды. Представьте, что нам нужно проверить, является ли строка предло­жением. Чтобы удовлетворять нашим требованиям строка должна начинаться с боль­шой буквы, содержать любое число букв верхнего и нижнего регистра и пробелов и заканчиваться точкой. Для поиска совпадений с этим (очень приблизительным) определением предложения воспользуемся следующим регулярным выражением: Выражение состоит из трех элементов: выражение в квадратных скобках с клас­сом символов [:upper:], выражение в квадратных скобках с двумя классами символов, [:upper:] и [:lower:], и пробелом, и точка, экранированная обратным слешем Второй элемент сопровождается метасимволом *, поэтому в нашем пред­ложении ему может соответствовать любое число букв верхнего и нижнего реги­стра и пробелов, следующих за первой буквой верхнего регистра: Первые два примера соответствуют выражению, а третье — нет, потому что в нем отсутствует обязательный первый символ верхнего регистра и завершающая точка.

Практические примеры применения регулярных выражений

Рассмотрим несколько уже знакомых команд и посмотрим, как они могут исполь­зовать регулярные выражения В предыдущем примере мы брали телефонные номера по одному и проверяли правильность их оформления. На практике же часто приходится проверять списки телефонов, поэтому давайте создадим такой список. Для этого восполь­зуемся волшебной магией командной строки. Магией, потому что мы еще не знакомы с большинством команд, привлеченных для решения поставленной задачи, но не волнуйтесь — мы рассмотрим их в последующих главах. Вот это волшебство: Эта команда создаст файл с именем phonelist. txt, содержащий 10 телефонных но­меров. Если повторить команду, она добавит в список еще 10 номеров. Также мож­но изменить число 10 ближе к началу команды, чтобы создать больше или меньше номеров. Однако если заглянуть в файл, можно заметить проблему: Некоторые номера оформлены неправильно, что очень хорошо для целей демон­страции их проверки с помощью grep. Метасимвол + действует почти так же, как *, но требует совпадения с предыдущим элементом не менее одного раза. Следующему регулярному выражению будут со­ответствовать только строки, состоящие из групп, насчитывающих один или не­сколько алфавитных символов и разделенных одиночными пробелами: Как видите, этому выражению не соответствует строка "a b 9", потому что она содержит неалфавитный символ; точно так же ему не соответствует строка "abc d", потому что между символами с и d в ней присутствует больше одного пробела Метасимволы { и } используются, чтобы выразить минимальное и максимальное число обязательных совпадений. Эти числа можно представить четырьмя воз­можными способами, как Возвращаясь к примеру с телефонными номерами, мы воспользуемся этим ме­тодом определения повторений, чтобы упростить исходное регулярное выра­жжение Как видите, измененная версия регулярного выражения успешно справляется с проверкой номеров, с круглыми скобками и без них, и отвергает неправильно оформленные номера Было бы полезно просканировать файл в поисках недопустимых номеров и вы­вести их

Здесь мы использовали параметр - v, чтобы обратить сопоставление и вывести только строки, не соответствующие указанному выражению Само выражение содержит якорные метасимволы на обоих концах и тем самым гарантирует отсутствие дополнительных символов слева и справа от номера. Кро­ме того, в отличие от примера, приведенного выше, это выражение также требует обязательного наличия круглых скобок в номере

Команда find поддерживает проверку, основанную на регулярном выражении. Су­ществует одно важное обстоятельство, которое следует помнить, используя регу­лярные выражения в командах find и grep. Если grep выводит строку, содержащую совпадение с регулярным выражением, то find требует точного совпадения пути с регулярным выражением. В следующем примере команда find использует регу­лярное выражение для поиска путей к файлам, содержащим любые символы, не входящие в следующее множество: Программа locate поддерживает простые (параметр --regexp) и расширенные (параметр --regex) регулярные выражения. Благодаря этому можно выполнять те же операции, что производились выше с файлами dirlist: Использовав чередование, мы нашли пути, содержащие bin/bz, bin/gz или /bin/zip.

Поиск текста в less и vim less и vim поддерживают одинаковые способы поиска в тексте. Чтобы выполнить поиск, нажмите клавишу / и введите регулярное выражение. Воспользуемся про­граммой less, чтобы просмотреть содержимое файла phonelist. txt: Как видите, выражение практически то же самое; различия обусловлены лишь тем, что многие символы, которые в расширенной версии выражений считаются метасимволами, в простой версии интерпретируются как литералы Они действу­ют как метасимволы, только если экранировать их символом обратного слеша В зависимости от конкретных настроек vim совпадения могут быть выделены или нет. Если совпадения не выделяются, попробуйте в командном режиме выполнить команду :hlsearch, чтобы активировать выделение результатов поиска. В этой главе мы рассмотрели несколько примеров использования регулярных вы­ражений Круг практического применения регулярных выражений можно расши­рить еще больше, если задействовать их для поиска в других приложениях, под­держивающих такую возможность Например, с их помощью можно выполнять поиск на страницах справочного руководства: Программа zgrep реализует интерфейс к grep, позволяя читать сжатые файлы. В данном примере выполняется поиск в сжатых файлах первого раздела справоч­ного руководства. Результатом этой команды будет список файлов, содержащих строку regex или regular expression. Как видите, регулярные выражения под­держиваются множеством программ Простые регулярные выражения обладают одной интересной особенностью, ко­торую мы пропустили, — обратными ссылками (back references). Они будут рас­сматриваться в следующей главе. Все Unix-подобные операционные системы широко используют текстовые фай­лы для хранения данных разных типов. Этим объясняется такое большое разно­образие инструментов обработки текста В этой главе мы рассмотрим программы, которые используются для выполнения самых разных манипуляций с текстом В следующей главе мы продолжим знакомство со средствами обработки текста, уделив больше внимания программам форматирования текста перед печатью и программам, удовлетворяющим другие потребности человека. В этой главе мы повторно рассмотрим уже знакомые программы и познакомимся с новыми: К настоящему моменту мы познакомились с парой текстовых редакторов (nano и vim), рассмотрели несколько конфигурационных файлов и увидели вывод не­скольких десятков команд, и все это в текстовом виде. А для чего еще использует­ся текст? Как оказывается, много для чего. Многие люди записывают документы в простом текстовом формате. Очевидно, достаточно удобно хранить простые заметки в небольших текстовых файлах, од­нако и большие документы также можно записывать в простом текстовом фор­мате Один из популярных подходов состоит в том, чтобы записать большой до­кумент в текстовом формате и затем использовать язык разметки для описания форматирования конечного документа Многие научные статьи написаны подоб­ным способом, так как системы обработки текста на основе Unix были в числе пер­вых, обеспечивших улучшенную поддержку типографического оформления, так необходимого авторам в технических дисциплинах

Веб-страницы

Самым популярным в мире форматом электронных документов является, пожа­луй, формат веб-страниц Веб-страницы — это текстовые документы с разметкой HTML (Hypertext Markup Language — язык разметки гипертекста) или XML (Extensible Markup Language — расширяемый язык разметки), описывающей ви­зуальный формат документа Электронная почта является текстовой средой по своей природе. Даже нетексто­вые вложения преобразуются в текстовое представление перед передачей В этом можно убедиться, загрузив электронное письмо и просмотрев его с помощью less. Вы увидите, что письмо начинается с заголовка, описывающего отправителя письма и промежуточные серверы, принимавшие его в процессе доставки, за кото­рым следует тело письма с его содержимым В Unix-подобных системах данные выводятся на печать в простом текстовом виде, или, если страница содержит графику, она преобразуется в описание стра­ницы в текстовом формате, известном как PostScript, которое затем посылается программе, генерирующей графические точки для печати на бумаге Многие программы командной строки, имеющиеся в Unix-подобных системах, были созданы для поддержки системного администрирования и разработки программного обеспечения, и программы обработки текста не исключение. Многие из них предна­значались для решения задач, связанных с разработкой программного обеспечения. Важность обработки текста для программистов объясняется тем, что любое про­граммное обеспечение начинает свое существование как текст. Исходный код, часть программы, которую пишет программист, всегда имеет текстовый формат мы познакомились с некоторыми командами, способными принимать дан­ные не только из аргументов командной строки, но и из стандартного ввода В той главе мы очень коротко познакомились с ними, но сейчас пришло время более близ­кого знакомства — мы узнаем, как их можно использовать для обработки текста Программа cat содержит множество интересных параметров Многие из них ис­пользуются для улучшенного отображения текстового содержимого Примером может служить параметр - A, используемый для отображения непечатаемых сим­волов в тексте Иногда необходимо знать, имеются ли управляющие символы в просматриваемом тексте. Наиболее распространенными из них являются сим­волы табуляции (в противоположность пробелам) и символы возврата каретки, часто представляющие концы строк в текстовых файлах, оформленных в стиле MS-DOS. Другим распространенным вариантом является файл, содержащий строки с завершающими пробелами Давайте создадим файл для экспериментов, используя cat как примитивный тек­стовый процессор Для этого введем команду cat (указав файл для перенаправ­ления вывода), а следом наш текст, завершив строки нажатием клавиши ENTER и закончив все комбинацией CTRL+D — она сообщит программе cat, что достиг­нут конец файла В этом примере мы ввели символ табуляции и добавили в конец строки несколько пробелов: Как видите, символ табуляции в тексте представлен парой символов Л1. Эта обыч­ная форма записи означает «CTRL+I», то есть, как оказывается, — символ табуля­ции Здесь также видно, что символ $ отмечает истинный конец строки, помогая увидеть дополнительные пробелы в конце строки

Sort — сортировка строк текстовых файлов

Программа sort сортирует содержимое стандартного ввода или одного или не­скольких файлов, указанных в командной строке, и записывает результаты в стан­дартный вывод Применив тот же прием, который мы использовали совместно с командой cat, можно продемонстрировать обработку стандартного ввода. После запуска команды мы ввели буквы c, b и а, а затем признак конца файла с по­мощью комбинации CTRL+D. Затем просмотрели получившийся файл и увидели, что строки в нем отсортированы Поскольку sort может принимать несколько файлов в аргументах командной строки, существует возможность объединить множество файлов в один отсорти­рованный файл. Например, если у вас имеется три файла и вам нужно объединить их в один отсортированный файл, это можно выполнить следующим образом: Программа sort имеет несколько интересных параметров. Их неполный список приводится в Имена большинства параметров из представленных выше говорят сами за себя, однако некоторые требуют дополнительных пояснений Прежде всего рассмо­трим параметр - n, используемый для сортировки по числовым значениям. Этот параметр позволяет сортировать строки по их числовым значениям Продемон­стрировать действие этого параметра можно на примере сортировки результатов команды du, чтобы определить каталог, занимающий больший объем дискового пространства Обычно команда du выводит результаты, отсортированные по име­нам каталогов: В этом примере мы передали результаты по конвейеру программе head, чтобы ограничить число результатов первыми 10 строками Мы можем изменить эту команду, добавив сортировку по числовым значениям, чтобы получить 10 самых объемных каталогов: С помощью параметров - nr мы получили сортировку по числовым значениям в обратном порядке, в результате наибольшие значения оказались в начале спи­ска. Такой способ сортировки стал возможен, потому что числовые значения на­ходятся в начале каждой строки. Но как быть, если потребуется отсортировать строки по числовым значениям, находящимся в середине строки, как, например, в результатах команды ls - l? Программа sort часто вовлекается в обработку табличных данных, таких как ре­зультат команды ls выше. Если воспользоваться терминологией баз данных, об этой таблице можно сказать, что каждая строка — это запись и каждая запись со­стоит из множества полей, таких как атрибуты файла, счетчик ссылок, имя файла, размер файла и т д Программа sort способна обрабатывать поля по отдельно­сти. Согласно той же терминологии баз данных, мы можем указать одно или не­сколько ключевых полей, которые должны использоваться как ключи сортировки. В примере, приведенном выше, мы добавили параметры n и г, чтобы выполнить сортировку по числовым значениям в порядке убывания, а также параметр - k с ар­гументом 5, чтобы указать, что сортировка должна выполняться по пятому полю. Параметр очень интересен и обладает множеством любопытных свойств, но пре­жде чем приступить к их обсуждению, поговорим о том, как sort определяет поля. Рассмотрим очень простой текстовый файл, содержащий единственную строку с именем автора этой книги: По умолчанию sort «видит» в этой строке два поля. Первое поле содержит после­довательность символов William, второе — последовательность символов Shotts, то есть пробельные символы (пробелы и символы табуляции) интерпретируются как разделители полей, и эти разделители включаются в поле при выполнении сортировки Взглянув еще раз на любую строку в выводе нашей команды ls, можно сказать, что она содержит восемь полей и пятое поле хранит размер файла: Для следующей серии экспериментов возьмем файл с историей выпуска новых версий трех популярных дистрибутивов Linux в период с 2006 по 2008 год. Каж­дая строка в файле содержит три поля: название дистрибутива, номер версии и дата выпускав формате

С помощью текстового редактора (например, vim) введите эти данные и сохраните в файле с именем distros. txt. Далее попробуем отсортировать файл и посмотрим, что из этого получится: [me@linuxbox ~]$ У нас это почти получилось. Единственная проблема возникла с сортировкой номеров версий Fedora. Так как в лексикографическом смысле 1 предшествует 5, версия 10 оказалась вверху, тогда как версии 9 — внизу. Чтобы исправить эту ошибку, выполним сортировку по нескольким ключам. Итак, нам нужно выполнить сортировку по первому полю в алфавитном порядке, а затем по второму полю в числовом порядке. Программа sort позволяет указать в командной строке несколько параметров - k, чтобы можно было определить не­сколько ключей сортировки. В действительности в ключ можно включать диа­пазон полей Если диапазон не определен (как в примерах, приведенных выше), sort использует в качестве ключа часть строки, начинающуюся с указанного поля и простирающуюся до конца строки. Вот как выглядит синтаксис сортировки по нескольким ключам: Здесь для ясности использовались имена параметров в длинной форме, однако с тем же успехом можно было бы передать параметры - k 1,1 - k 2n. В аргументе для первого экземпляра параметра ключа мы указали диапазон полей, входящих в первый ключ. Так как сортировка должна выполняться только по первому полю, мы указали диапазон 1,1, что означает: «начиная с поля 1 и заканчивая полем 1». Второму экземпляру мы передали аргумент 2n, который означает: «ключом сорти­ровки является второе поле и сортировка выполняется в порядке числовых зна­чений» . В конце определения ключа допускаются однобуквенные имена параме­тров, они указывают на тип сортировки. Имена этих однобуквенных параметров совпадают с именами глобальных параметров программы sort: b (игнорировать начальные пробелы), n (числовая сортировка), r (сортировка в обратном поряд­ке) и т д

Uniq — выявление или удаление повторяющихся строк

В сравнении с sort программа uniq более легковесна. Она решает, казалось бы, тривиальную задачу. Когда ей передается сортированный файл (в том числе и стандартный ввод), она удаляет повторяющиеся строки и выводит результат в стандартный вывод. Она часто используется в сочетании с sort для удаления повторяющихся строк Третье поле в списке содержит дату в формате, неудобном для сортировки В ком­пьютере даты обычно приводятся к виду ГГГГ-ММ-ДД, что упрощает сортировку в хронологическом порядке, но здесь используется американский формат ММ/ ДД/ГГГГ. Как же тогда отсортировать этот список в хронологическом порядке? К счастью, sort предоставляет такую возможность. Параметр --key позволяет определять смещения внутри полей, чтобы в качестве ключей можно было ис­пользовать части полей: Добавив параметр - k 3.7, мы сообщили программе sort, что она должна исполь­зовать для сортировки ключ, начинающийся с седьмого символа в третьем поле, который соответствует началу года. Аналогично, параметры - k 3.1 и - k 3.4 опре­деляют ключи сортировки по месяцу и дню месяца. Мы также добавили параме­тры n и r, чтобы выполнить числовую сортировку в обратном порядке. Параметр b добавлен для исключения начальных пробелов из поля с датой (число которых в разных строках отличается и тем самым влияет на результат сортировки) . В некоторых файлах в качестве разделителей используются символы, отличные от пробелов и символов табуляции; возьмем, к примеру, файл /etc/passwd: Поля в этом файле отделяются друг от друга символом двоеточия (:) . Можно ли отсортировать содержимое этого файла с использованием ключевых полей? Программа sort поддерживает параметр - t для определения символа-разделителя по­лей Чтобы отсортировать содержимое файла passwd по седьмому полю (команд­ная оболочка по умолчанию), используем такую команду: Определив двоеточие как разделитель полей, мы смогли выполнить сортировку по седьмому полю Не забудьте ввести CTRL+D, чтобы завершить ввод с клавиатуры. Если теперь при­менить uniq к нашему текстовому файлу, результат ничем не будет отличаться от оригинала; повторяющиеся записи никуда не исчезли:Чтобы uniq действительно выполнила свою работу, исходные данные нужно сна­чала отсортировать: Это объясняется тем, что uniq удаляет повторяющиеся записи, только если они следуют друг за другом uniq имеет несколько параметров Наиболее часто используемые из них перечис­лены. В следующем примере используется параметр - c программы uniq для определе­ния числа повторяющихся строк в исходном текстовом файле: [me@linuxbox ~]$ sort foo. txt | uniq –c Далее мы обсудим три программы, которые используются для выделения колонок текста из файлов и их компоновки различными способами Программа cut используется для извлечения фрагментов текста из строк и выво­да их в стандартный вывод Она может принимать имена файлов в аргументах или данные со стандартного ввода Определение фрагментов строк, подлежащих извлечению, реализовано не очень удобно, и для этой цели применяются параметры, перечисленные В присутствии параметра - f в качестве разделителя полей используется символ_разделитель. По умолчанию поля должны отделяться друг от друга одним символом табуля­ции—complement Извлекает строку текста целиком, кроме фрагментов, опре­деляемых параметром - c и/или - f

Как видите, программа cut не обладает особенной гибкостью Она лучше всего подходит для извлечения фрагментов из текста, произведенного другими про­граммами, а не человеком Давайте вернемся к нашему файлу distros. txt и по­смотрим, достаточно ли он «хорош» для программы cut. Если воспользоваться программой cat с параметром - A, можно увидеть, отвечает ли файл требованию в отношении использования символа табуляции в качестве разделителя полей Похоже, что все в порядке: пробелы отсутствуют и поля разделены единствен­ными символами табуляции Поскольку вместо пробелов в файле используются символы табуляции, можно воспользоваться параметром - f для извлечения поля

Поля в файле distros. txt

Так как поля в файле distros. txt разделены символами табуляции, их удобнее из­влекать с помощью cut именно как поля, а не как группы символов Когда поля разделяются символами табуляции, маловероятно, что строки будут содержать одно и то же число символов, из-за чего определение позиций символов в строках становится сложной или неразрешимой задачей В примере, приведенном выше, мы смогли извлечь поля с датами, которые, к нашей удаче, все имеют одинаковую длину, поэтому теперь мы можем показать, как выполняется извлечение групп символов, для чего попробуем извлечь год из каждой строки: Применив cut второй раз к нашему списку, мы смогли извлечь символы в позициях с 7-й по 10-ю, которые соответствуют году в поле с датой. Форма записи 7-10 — это пример определения диапазона. Полное описание особенностей определения диа­пазонов находится на странице справочного руководства (man) для команды cut При работе с полями определим разделитель, отличающийся от символа табуля­ции Следующий пример извлекает первое поле из файла /etc/passwd: С помощью параметра - d мы определили, что роль разделителя полей будет играть символ двоеточия Наш файл distros. txt идеально отформатирован для извлечения полей с использованием cut. Но что, если нам понадобится обработать файл, вырезая фрагменты по символам, а не по полям? Для этого нам нужно заменить символы табуляции в файле соответ­ствующим числом пробелов. К счастью, в GNU-пакете coreutils имеется инструмент для этого — программа expand. Она может принимать имена файлов в аргументах или данные со стандартного ввода и выводить измененный текст в стандартный вывод. Если обработать наш файл distros. txt программой expand, мы сможем использовать cut - c для извлечения любых диапазонов символов из файла. Например, с помощью следующей команды можно извлечь год выпуска из нашего файла со списком, применив cut для извлечения всех символов с 23-й позиции до конца строки: [me@linuxbox ~]$ expand distros. txt | cut - c 23­В состав пакета coreutils входит также программа unexpand, замещающая пробелы символами табуляции. Команда paste выполняет операцию, обратную команде cut. Вместо извлечения колонок текста из файла она добавляет одну или несколько колонок текста в файл Для этого она читает содержимое нескольких файлов, комбинирует поля, найден­ные в них, и выводит результат в стандартный вывод Подобно программе cut, paste принимает несколько файлов в аргументах и/или данные со стандартного ввода. Для демонстрации возможностей программы paste выполним небольшую хирургическую операцию с файлом distros. txt, чтобы получить список выпусков в хронологическом порядке Сначала применим команду sort, чтобы получить список дистрибутивов, отсор­тированный по дате выпуска, и сохраним результат в файле distros-by-date. txt: Затем с помощью cut извлечем два первых поля (с именами дистрибутивов и но­мерами версий) и сохраним результат в файле distro-versions. txt: В заключение этапа подготовки извлечем даты выпусков и сохраним их в файле distro-dates. txt: Теперь у нас есть все необходимое. Чтобы завершить процедуру, с помощью paste добавим колонку с датами перед названиями и номерами версий дистрибутивов, создав хронологический список. Для этого достаточно просто вызвать paste и пе­редать ей файлы в требуемом порядке

join — объединение строк из двух файлов по общему полю

Программа join действует подобно paste, в том смысле, что она добавляет колон­ки в файл, но делает это по-своему. Операция join у многих ассоциируется с ре­ляционными базами данных, где она объединяет записи из нескольких таблиц по общему ключевому полю Программа join выполняет ту же операцию Она объ­единяет данные из множества файлов, опираясь на общее ключевое поле Чтобы понять, как действует операция join в реляционной базе данных, пред­ставьте очень маленькую базу данных с двумя таблицами, по одной записи в каж­дой. Первая таблица, с именем CUSTOMERS, имеет три поля: номер клиента (CUSTNUM), имя клиента (FNAME) и фамилия клиента (LNAME): Обратите внимание, что обе таблицы имеют общее поле CUSTNUM. Это важно, так как оно устанавливает отношение между таблицами Применив операцию join, мы сможем объединить поля из двух таблиц, чтобы получить желаемый результат, например, для подготовки накладной Проверяя совпадение значений в полях CUSTNUM обеих таблиц, операция join выдаст следу­ющий результат: Для демонстрации программы join нам понадобится пара файлов с общим клю­чом. Возьмем в качестве отправной точки файл distros-by-date. txt и из него скон­струируем два дополнительных файла Первый будет содержать даты выпусков (которые в этом примере будут играть роль общего ключа) и названия дистрибу­тивов: Теперь у нас есть два файла с общим ключом (поле «дата выпуска») . Здесь важ­но отметить, что файлы должны быть отсортированы по ключевому полю, чтобы программа join выдала правильный результат. Отметьте также, что по умолчанию в качестве разделителя полей во входных данных join использует символы табуляции, а в выводе — пробел. Такое поведение можно изменить с помощью параметров За дополнительными подробностями об­ращайтесь к странице справочного руководства (man) для join. Довольно часто бывает необходимо сравнить версии текстовых файлов. Для си­стемных администраторов и разработчиков программного обеспечения это осо­бенно важно. Системному администратору, например, может понадобиться срав­нить имеющийся конфигурационный файл с предыдущей версией, чтобы понять суть возникшей проблемы. Аналогично, программисту часто бывает необходимо увидеть изменения, происшедшие в программе с течением времени Программа comm сравнивает два текстовых файла, показывая, какие строки в них уникальные, а какие — одинаковые. Для демонстрации создадим с помощью cat два почти идентичных файла: Как видите, comm произвела вывод в три колонки. Первая колонка содержит уни­кальные строки из первого файла, вторая — уникальные строки из второго фай­ла, третья — строки, одинаковые в обоих файлах. Программа comm поддерживает параметры в формате - n, где n может быть числом 1, 2 или 3 . При использовании эти параметры определяют номера колонок, вывод которых следует подавить Например, чтобы вывести только одинаковые строки, нужно подавить вывод колонок 1 и 2: Подобно программе comm, diff используется для выявления различий между фай­лами. Однако diff намного более сложный инструмент, поддерживающий вывод во множестве форматов и способный обрабатывать сразу огромные коллекции файлов. Программа diff часто используется разработчиками программного обе­спечения для исследования различий между разными версиями исходного про­граммного кода, потому что позволяет рекурсивно обходить каталоги, которые часто называют деревьями исходного кода (source trees) . Часто программа diff применяется для создания diff-файлов, или заплат (patches), которые могут ис­пользоваться другими программами, такими как patch (о которой рассказывается чуть ниже), для преобразования файлов из одной версии в другую Если применить diff к файлам из предыдущего примера, можно увидеть стиль вывода результатов ее работы по умолчанию: краткое описание различий между двумя файлами В формате по умолчанию каждой группе изменений предшествует команда изме­нения в форме диапазон — операция — диапазон, описывающей пози­ции и типы изменений, которые нужно выполнить, чтобы преобразовать первый файл во второй В этом формате любой диапазон представлен списком через запятую номеров начальной и конечной строки. Хотя этот формат используется по умолчанию (главным образом для совместимости со стандартом POSIX и обратной совме­стимости с традиционными версиями diff для Unix), он не так широко исполь­зуется, как другие, дополнительные форматы. Два других формата, получив­ших большую популярность, — это контекстный формат и унифицированный формат

Использование контекстного формата

При использовании контекстного формата (параметр - с) вывод выглядит так:

Вывод начинается с имен двух файлов и времени последнего их изменения. Пер­вый файл отмечается звездочками, а второй — дефисами. На протяжении всей оставшейся части листинга эти маркеры обозначают соответствующие им файлы. Далее следуют группы изменений, включая заданное по умолчанию число окру­жающих строк, определяющих контекст Индикаторы изменений, генерируемые программой diff при использовании контекстного формата Унифицированный формат напоминает контекстный, но более компактный. За­дается параметром - u: Самое большое отличие между контекстным и унифицированным форматами — отсутствие повторяющихся контекстных строк, благодаря чему обеспечивается большая компактность унифицированного формата в сравнении с контекстным В примере, приведенном выше, видны те же времена последнего изменения фай­лов, что и в контекстном формате, за которыми следует строка @@ -1,4 +1,4 @@. Она указывает номера строк в первом и во втором файлах, описываемых группой изменений. Далее следуют сами строки с тремя (по умолчанию) строками контек­ста. Каждая строка начинается с одного из трех возможных символов, значение которых описывается Программа patch используется для применения изменений к текстовым файлам Она принимает вывод программы diff и обычно используется для преобразования старых версий файлов в более новые. Рассмотрим один известный пример. Ядро Linux разрабатывается большой, свободно организованной группой разработчи­ков, от которых неиссякаемым потоком идут небольшие изменения в исходном коде Ядро Linux включает миллионы строк программного кода, но изменения, присылаемые одним разработчиком за один раз, очень невелики Разработчикам нет смысла пересылать все дерево исходных текстов ядра всякий раз, когда вно­сится небольшое изменение. Вместо этого они присылают diff-файлы. Эти фай­лы описывают различия между предыдущей версией ядра и новой, включающей изменения разработчика Другой разработчик, получивший такое изменение, ис­пользует программу patch, позволяющую применить предложенное изменение к своему дереву исходных текстов. Использование пары программ diff/patch дает два важных преимущества: применить эти изменения и оценить их Разумеется, пару diff/patch можно применять к любым текстовым файлам, не только к исходному коду. Эти программы с таким же успехом можно применять к конфигурационным файлам или другому тексту Чтобы подготовить diff-файл для последующего его применения программой patch, документация GNU предлагает использовать diff, как показано ниже: где старый_файл и новый_файл могут быть одиночными файлами или каталогами. Параметр r поддерживает рекурсивный обход вложенных подкаталогов. В этом примере мы создали diff-файл с именем patchfile. txt и затем воспользовались программой patch, чтобы применить его (наложить «заплату»). Обратите внима­ние, что нам не пришлось указывать целевой файл, потому что diff-файл (в унифи­цированном формате) уже содержит имена файлов в заголовке После наложения «заплаты» содержимое file1.txt точно соответствует содержимому file2.txt. Программа patch имеет большое число параметров, а кроме того, существует множество вспомогательных программ, которые помогут в правке «заплат» (diff - файлов)

Редактирование на лету

Наш опыт использования текстовых редакторов ограничивается в основном интерактивным способом их использования, в том смысле, что мы вручную перемещаем курсор в нужное место и затем вносим необходимые изменения Од­нако существуют также неинтерактивные способы редактирования текста. Впол­не возможно, например, применить серию изменений к множеству файлов всего одной командой Программа tr используется для перекодирования символов. Ее можно рассма­тривать как своеобразную посимвольную операцию поиска с заменой Переко­дирование — это процесс замены символов из одного алфавита символами из другого алфавита. Например, преобразование символов из нижнего регистра в верхний — это перекодирование. Такое преобразование можно выполнить с помощью tr: Как видите, tr принимает исходные данные со стандартного ввода и выводит ре­зультаты в стандартный вывод tr принимает два аргумента: множество символов, подлежащих преобразованию, и соответствующее множество символов, в кото­рые должны превратиться преобразуемые символы. Множества символов можно выразить тремя способами: В большинстве случаев множества символов должны иметь одинаковую длину; что вполне допустимо в случае, если первое множество оказывается больше вто­рого Это может пригодиться, например, если существует необходимость преоб­разования нескольких символов в один: Кроме перекодирования tr позволяет просто удалять символы из входного пото­ка. Выше в этой главе мы обсуждали проблему преобразования текстовых файлов в формате MS-DOS в текст в формате Unix. Для такого преобразования достаточ­но просто удалить символы возврата каретки в конце каждой строки Эту опера­цию можно выполнить с помощью tr: Одно забавное применение команды tr — шифрование текста по алгоритму ROT13. ROT13 — тривиальный тип шифрования, основанный на простом подстановочном шиф­ре. Шифрованием назвать этот алгоритм можно только с большой натяжкой, скорее это алгоритм обфускации (запутывания) текста. Он используется иногда для запуты­вания потенциально уязвимого содержимого. Метод заключается в простом смещении каждого символа на 13 позиций далее по алфавиту. Так как число 13 соответствует середине набора из 26 символов, повторное применение алгоритма к тексту приводит к его восстановлению в исходное состояние. Шифрование с помощью tr выполняется, как показано ниже:

Многие программы для работы с электронной почтой и чтения новостей Usenet поддерживают шифрование ROT13. В Википедии можно найти замечательную статью по этой теме: tr также позволяет выполнять и другие трюки. При вызове с параметром - s коман­да tr «сжимает» (squeeze), или удаляет повторяющиеся экземпляры символов: Здесь у нас имеется строка с повторяющимися символами. Передав множество ab команде tr, мы удалили повторяющиеся экземпляры символов, входящие в мно­жество, при этом остальные символы (c), отсутствующие в множестве, остались нетронутыми Обратите внимание, что повторяющиеся символы должны следо­вать подряд В противном случае сжатие не даст никакого эффекта:

Sed — потоковый редактор для фильтрации и преобразования текста

Имя sed — это сокращенное словосочетание stream editor (потоковый редактор) . Данная команда осуществляет редактирование потока текста, получаемого из множества файлов или подаваемого на стандартный ввод команды. sed — мощная и достаточно сложная программа (ей посвящены целые книги), поэтому здесь мы не будем рассматривать ее во всех подробностях В общем случае sed используется следующим образом: ей передается единствен­ная команда редактирования (в командной строке) или имя файла сценария с множеством команд, и она применяет эти команды к каждой строке в потоке текста. Ниже приводится очень простой пример sed в действии: В этом примере с помощью echo создается поток текста с единственным словом, который по конвейеру передается программе sed. sed, в свою очередь, применяет инструкцию s/front/back/ к тексту в потоке и выводит результат. Эта команда напоминает команду подстановки (поиск с заменой) в редакторе vi

Команды sed начинаются с единственной буквы В примере, рассмотренном выше, буква s представляет команду подстановки (substitution) . За ней следуют искомая строка и строка замены, разделенные слешем В качестве разделителя можно использовать любые символы По общепринятому соглашению, чаще дру­гих используется символ слеша, но sed будет использовать в качестве разделителя любой символ, следующий сразу за командой Ту же самую команду можно было бы записать иначе: Символ подчеркивания, следующий сразу за командой, становится разделителем Возможность употребления произвольных разделителей можно использовать для улучшения читаемости команд, как будет показано далее Большинству команд в sed может предшествовать адрес, который определяет, какие строки во входном потоке должны редактироваться Если адрес отсутствует, коман­да редактирования применяется ко всем строкам во входном потоке В простейшем случае адрес — это номер строки Мы могли бы добавить единицу в наш пример: Добавление адреса 1 в команду гарантирует применение операции подстановки только к первой строке в нашем однострочном потоке Можно указать другое число: [me@linuxbox ~]$ Теперь, как видите, редактирование не было выполнено, потому что во входном потоке отсутствует строка с номером 2. Рассмотрим разные способы адресации строк на примере файла distros. txt, создан­ного выше в этой главе. Сначала попробуем диапазоны номеров строк: В нашем примере мы вывели строки с 1 по 5 . Для этого использовалась коман­да p, которая просто выводит строки, соответствующие адресам. Однако здесь нам пришлось добавить параметр - n (параметр подавления автоматического вывода), чтобы программа sed не выводила все строки, что она делает по умолчанию Включив регулярное выражение /SUSE/, заключенное в символы слеша, мы смог­ли выделить строки подобно тому, как это делает программа grep.

Наконец, попробуем применить оператор отрицания, добавив в адрес восклица­тельный знак (!): Команда s, вне всяких сомнений, используется намного чаще других команд редактирования. Далее мы рассмотрим только часть ее возможностей, выполняя редактирование нашего файла distros. txt. Мы уже говорили, что поле даты в distros. txt хранит информацию не в самом «дружественном» для компьютеров виде. Здесь даты записаны в формате ММ/ДД/ГГГГ, однако гораздо удобнее (для сортиров­ки) было бы, если бы даты были записаны в формате ГГГГ-ММ-ДД. Замена пред­ставления дат вручную — довольно утомительное занятие и чревато ошибками, но с помощью sed ту же замену можно выполнить в одно действие: Прекрасный результат! Правда, команда выглядит устрашающе, но она работает. За один шаг мы изменили представление дат во всем файле. Этот пример также наглядно показывает, почему про регулярные выражения иногда в шутку говорят «только для записи». Мы можем писать их, но прочитать их порой никак не полу­чается Прежде чем сбежать от этой устрашающей команды, давайте посмотрим, как она была сконструирована Во-первых, как мы уже знаем, эта команда имеет следующую структуру: Теперь разберем регулярное выражение, отыскивающее даты. Так как даты имеют формат ММ/ДД/ГГГГ и находятся в конце строки, найти их можно с помощью следующего выражения: которому соответствуют две цифры, слеш, две цифры, слеш, четыре цифры и ко­нец строки. Так, с регулярным выражением разобрались, а что со строкой замены? Чтобы описать ее, нам необходимо познакомиться с новой для нас особенностью регулярных выражений, которую можно использовать в некоторых приложениях, поддерживающих BRE. Эта особенность называется обратные ссылки, и действу­ет она так: если в строке замены присутствует последовательность \n, где n — чис­ло от одного до девяти, эта последовательность будет ссылаться на совпадение с соответствующим подвыражением в предшествующем регулярном выражении. Чтобы создать подвыражение, достаточно просто заключить часть регулярного выражения в круглые скобки, например: Другая особенность команды s — возможность использования дополнительных флагов вслед за строкой замены. Наиболее примечательным из них является флаг g, который требует от sed применить поиск с заменой к строке глобально (globally), а не только к первому найденному совпадению, как это делается по умолчанию. Как видите, замена была выполнена только для первого вхождения буквы b, а остальные остались нетронутыми. Добавив флаг g, можно изменить все вхож­дения

Aspell — интерактивная проверка орфографии

Последний инструмент, который мы рассмотрим в этой главе, — программа aspell, интерактивное средство проверки орфографии. Программа aspell является пре­емницей программы ispell, существовавшей прежде, и может использоваться как ее замена. Чаще всего программа aspell используется другими программами в тех случаях, когда необходима функция проверки орфографии, однако aspell может также весьма эффективно использоваться как самостоятельный инстру­мент командной строки. Она способна проверять текстовые файлы разных типов, включая документы HTML, программы на C/C++, электронные письма и другие специальные виды текста Чтобы проверить орфографию в файле с простым текстом, можно вызвать aspell, как показано ниже: где текстовый_файл — это имя файла для проверки. В качестве практического при­мера создадим простой текстовый файл с именем foo. txt, содержащий несколько произвольных орфографических ошибок: Поскольку в режиме проверки (check) программа aspell действует интерактивно, вы увидите следующий экран: В верхней части экрана выводится текст с выделенным подозрительным словом В середине — 10 вариантов исправления ошибки, пронумерованных от 0 до 9, а затем следует список других возможных действий. Наконец, в самом низу выво­дится приглашение к вводу, готовое принять наш выбор Если ввести 1, aspell заменит подозрительное слово jimped словом jumped и перей­дет к следующему подозрительному слову, laxy. Если выбрать вариант замены lazy, aspell выполнить подстановку и завершится (так как ошибок во введенной фразе больше нет) . После того как aspell завершится, заглянем в файл и увидим, что все ошибки исправлены: Если вызвать программу aspell без параметра --dont-backup, она создаст резерв­ную копию файла с исходным текстом, добавив к имени файла расширение. bak. А теперь похвастаемся умением пользоваться программой sed и вернем наши ошибки на место, чтобы продолжить эксперименты с нашим файлом: Параметр - i сообщает программе sed, что требуется отредактировать файл «на месте», в том смысле, что изменения нужно произвести в самом файле, а не пере­слать их в стандартный вывод Здесь также показана возможность передать более одной команды редактирования, разделив их точкой с запятой Далее мы посмотрим, как aspell справляется с текстовыми файлами разных ви­дов С помощью текстового редактора, например vim (наиболее смелые могут по­пробовать использовать sed), добавим в файл немного разметки HTML: Теперь теги HTML игнорируются, и проверке подвергаются только фрагменты файла, не являющиеся частью разметки. В этом режиме содержимое HTML-тегов игнорируется и не проверяется, исключение составляет содержимое тегов ALT (точнее, атрибутов alt), которое будет проверяться в этом режиме проверки. По умолчанию aspell игнорирует адреса URL и электронной почты в тексте. Эту ситу­ацию можно изменить с помощью параметров командной строки. Также можно указать, какие теги разметки должны проверяться, а какие пропускаться. За подробностями об­ращайтесь к странице справочного руководства (man) для aspell. В этой главе мы познакомились с несколькими из множества инструментов командной строки для обработки текста В следующей главе мы рассмотрим еще несколько Нужно признать, что для многих из вас пока не очевидно, как или для чего можно было бы использовать некоторые из них в повседневной работе, хотя мы попытались привести практические примеры В следующих главах вы увиди­те, что эти инструменты формируют базовый набор для решения большого коли­чества практических задач Это вам пригодится, когда мы перейдем к сценариям на языке командной оболочки, где эти инструменты по-настоящему продемон­стрируют свои возможности Существует несколько интересных команд обработки текста, на которые стоит об­ратить внимание. Среди них split (разбивает файлы на фрагменты), csplit (раз­бивает файлы на фрагменты, опираясь на контекст) и sdiff (выводит различия между файлами, что называется, «бок о бок»)

Форматирование вывода

В этой главе мы продолжим знакомство с инструментами, имеющими отноше­ние к тексту, сосредоточившись на программах для форматирования выводимого текста, а не его изменения Эти инструменты часто используются для подготовки текста к печати, о которой мы поговорим в следующей главе В этой главе мы рас­смотрим следующие программы: Для начала рассмотрим несколько инструментов простого форматирования В ос­новном это узкоспециализированные и довольно бесхитростные программы, но их можно использовать для решения простых задач в конвейерах и сценариях Программа nl — предназначена для решения простой задачи: она выполняет нуме­рацию строк. В простейшем случае использования nl напоминает команду cat - n: Так же, как cat, программа nl может принимать несколько имен файлов в аргу­ментах командной строки или данные со стандартного ввода. Однако nl имеет ряд параметров и поддерживает простейшую форму разметки, обеспечивая более сложные способы нумерации. nl поддерживает идею логических страниц. Это дает возможность начинать ну­мерацию на каждой странице заново. С помощью параметров можно определить номер первой строки и протяженность нумерации, а также формат номеров. Логи­ческую страницу можно разбить на заголовок, тело и нижний колонтитул В каж­дом разделе нумерация может начинаться с начала и/или использоваться разный формат нумерации. Если программе nl передать несколько файлов, она будет интерпретировать их как один поток текста. Разделы в потоке выделяются добавле­нием в поток немного странной разметки, как показано Каждый элемент разметки из представленных должен находиться в отдельной строке. После обработки элемента программа nl удалит его из потока текста инструментов для решения более сложных задач Возьмем за основу наши на­работки, созданные в предыдущей главе для получения отчета о дистрибутивах Linux. Поскольку далее мы будем использовать программу nl, включим в текст разметку, отделяющую заголовок/тело/нижний колонтитул. Для этого откройте в текстовом редакторе сценарий для sed из предыдущей главы, добавьте в него строки с разметкой, как показано ниже, и сохраните сценарий в файле с именем distros-nl. sed: Новый сценарий вставляет разметку логических страниц для nl и добавляет ниж­ний колонтитул в конец отчета. Обратите внимание, что нам пришлось удвоить символы обратного слеша в разметке, потому что sed обычно интерпретирует их как экранирующие символы Теперь выведем улучшенный отчет, объединив sort, sed и nl: Наш отчет является результатом объединения в конвейер нескольких команд Сначала мы отсортировали список по названиям дистрибутивов и номерам вер­сий (поля 1 и 2), затем обработали результат программой sed, добавив заголовок отчета (включая разметку логических страниц для nl) и нижний колонтитул. В заключение мы обработали результат с помощью программы nl, которая по умолчанию нумерует только строки в потоке текста, принадлежащие разделу с те­лом логической страницы Попробуйте повторить команду и поэкспериментировать с разными параметрами команды nl. Интересный результат, например, можно получить с помощью Перенос строк заключается в разрыве текстовых строк по указанной ширине. По­добно другим рассматриваемым командам, fold может принимать одно или не­сколько имен файлов или данные со стандартного ввода. Если передать команде fold простой поток текста, можно увидеть, как она действует: Здесь мы видим, как действует программа fold Текст, посланный командой echo, был разбит на сегменты указанной в параметре - w ширины. В этом при­мере мы ограничили ширину строк 12 символами. Если ширина не указана, по умолчанию она принимается равной 80 символам. Обратите внимание, что стро­ки были разбиты без учета границ слов. Добавив параметр - s, можно заставить fold разбивать строки по последнему доступному пробелу перед достижением указанной ширины:

Fmt — простое форматирование текста

Программа fmt также позволяет выполнить перенос строк плюс кое-что еще Она принимает файлы или данные со стандартного ввода и формирует абзацы в по­токе текста По сути, она заполняет и объединяет строки, сохраняя пустые строки и отступы Для демонстрации нам понадобится некий текст. Возьмем фрагмент из Info - страницы для fmt: 'fmt' читает текст из файла, заданного аргументами FILE (или со стандартного ввода, если аргументы отсутствуют), и выводит результат в стандартный вывод. 'fmt' читает текст из файла, заданного аргументами FILE (или со стандартного ввода, если аргументы отсутствуют), и выводит результат в стандартный вывод. По умолчанию пустые строки, пробелы между словами и отступы сохраняются в выводе; последующие строки с разными отступами не объединяются; символы табуляции на входе заменяются соответствующим.

Результат не особенно впечатляет. Может быть, стоит прочитать этот текст, так как он объясняет происходящее: По умолчанию пустые строки, пробелы между словами и отступы сохраняются в выво­де; последующие строки с разными отступами не объединяются; символы табуляции на входе заменяются соответствующим числом пробелов и выводятся в таком виде. Итак, fmt сохраняет отступ в первой строке. К счастью, fmt имеет параметр, исправляющий это:стандартного ввода, если аргументы отсутствуют), и выводит результат в стандартный вывод. По умолчанию пустые строки, пробелы между словами и отступы сохраняются в выводе; последующие строки с разными отступами не объединяются; символы табуляции на входе заменяются соответствующим числом пробелов и выводятся в таком виде. 'fmt' старается разбивать строки по концам предложений и стремится не разрывать строки после первого слова или перед последним словом в предложении. "Конец предложения" определяется либо по концу абзаца, либо по слову, завершающемуся любым из символов '.?!', за которым следуют два пробела или символ перевода строки, любые скобки или кавычки при этом игнорируются. Подобно TeX, 'fmt' читает "абзацы" целиком, прежде чем выполнить перенос строк; программа использует вариант алгоритма, предложенного Дональдом Э. Кнутом (Donald Намного лучше. Добавив параметр - c, мы получили желаемый результат. Программа fmt содержит несколько интересных параметров, которые перечисле­ны Особый интерес представляет параметр - p С его помощью можно форматировать выбранные фрагменты файла, гарантировав, что все отформатированные строки будут начинаться с одной и той же последовательности символов Многие языки программирования поддерживают комментарии, начинающиеся с символа решет­ки (#), и такие комментарии можно форматировать с помощью этого параметра Давайте создадим файл, имитирующий исходный текст программы с коммента­риями: Файл примера содержит комментарии, начинающиеся со строки # (символ # и пробел), и строки «кода» . Теперь воспользуемся командой fmt, чтобы отформа­тировать комментарии и при этом не затронуть код: Это не комментарий, а строка с кодом. Еще одна строка с кодом. И еще. Обратите внимание, что смежные строки комментариев были объединены, а пу­стые строки и строки, не начинающиеся с указанного префикса, остались нетро­нутыми Программа pr используется для разбивки текста на страницы. Перед печатью текст зачастую желательно разбить на страницы с несколькими пустыми строка­ми в начале и в конце, чтобы получить пустые поля вверху и внизу каждой страни­цы. В дальнейшем эти поля можно использовать для вставки верхнего и нижнего колонтитулов Продемонстрируем работу pr, форматируя наш файл distros. txt в последователь­ность очень коротких страниц (ниже показаны только первые две страницы): В этом примере использовались параметры - l (длина (length) страницы) и - w (ширина (width) страницы), определяющие размеры «страницы» — 65 символов в ширину и 15 строк в длину. pr разбила содержимое файла distros. txt на отдельные страницы, добавив несколько пустых строк сверху и снизу, и создала заголовок по умолчанию, содержащий время последнего изменения файла, имя файла и номер страницы Программа pr поддерживает множество параметров для управления форматированием страницы, но подробнее о них мы поговорим

Printf — форматирование и вывод данных

В отличие от других команд в этой главе, команда printf не используется в кон­вейерной обработке (она не принимает данные со стандартного ввода) и редко применяется непосредственно в командной строке (чаще она используется в сце­нариях) . Почему это так важно? Потому что она используется очень широко. Команда printf (ее название происходит от print formatted (форматированный вывод)) первоначально была создана как функция для языка программирова­ния C и впоследствии была реализована во многих других языках, включая язык командной оболочки Фактически в bash команда printf реализована как встроенная команда Она имеет следующий синтаксис: Команда принимает строку с описанием формата, которая затем применяется к списку аргументов Отформатированный результат передается в стандартный вывод. Ниже приводится простой пример: Строка формата может содержать литеральный текст (такой, как I formatted the string:), экранированные последовательности (такие, как \n, символ пере­вода строки) и последовательности, начинающиеся с символа %, которые называ­ют спецификаторами преобразований (conversion specifications) . В примере выше спецификатор преобразования %s используется для форматирования строки foo и включения ее в вывод команды. Еще один пример: Как видите, в выводе команды спецификатор преобразования %s замещается стро­кой foo. Преобразование s используется для форматирования строковых данных. Существуют также другие спецификаторы для других видов данных перечислены наиболее часто используемые типы данных Продемонстрируем действие каждого спецификатора преобразования на примере строки 380: Так как в строке формата указано шесть спецификаторов формата, нам потребова­лось передать команде printf шесть аргументов. Шесть результатов показывают результат действия каждого спецификатора Для настройки вывода спецификатору формата можно передать несколько до­полнительных компонентов Полный синтаксис спецификатора преобразования выглядит так: Для правильной интерпретации дополнительные компоненты, если их несколько, должны передаваться в указанном порядке. Все компоненты описаны. И еще раз: команда printf в основном используется в сценариях, где применяется для форматирования табличных данных, а не как самостоятельный инструмент командной строки. Тем не менее мы можем использовать ее для решения различ­ных задач форматирования. Во-первых, попробуем вывести несколько полей, раз­делив их символами табуляции: Добавив \t (экранированную последовательность, соответствующую символу та­буляции), мы достигли желаемого эффекта. Затем попробуем вывести несколько чисел в форматированном виде: Здесь демонстрируется действие компонента, определяющего минимальную ширину поля. А можно ли подобным образом отформатировать небольшую веб­страницу? До сих пор мы исследовали простые инструменты форматирования текста. Они хорошо подходят для решения небольших и простых задач, но как быть с более сложными заданиями? Одна из причин большой популярности операционной системы Unix среди технических специалистов и научных работников (кроме мощной поддержки многозадачности и многопользовательского окружения для выполнения любых работ, связанных с разработкой программного обеспечения) состоит в наличии инструментов, которые можно использовать для создания са­мых разных документов, таких как научные и академические публикации. Фак­тически, как описывается в документации GNU, средства подготовки документов положительно сказались на разработке Unix: Первая версия UNIX была создана на машине PDP-7, простаивавшей в Bell Labs. В 1971-м разработчики захотели заполучить PDP-11 для дальнейшей работы над операционной системой. Чтобы оправдать затраты на эту систему, они внес­ли предложение о реализации системы форматирования документов для па­тентного бюро в AT&T. Эта первая программа форматирования являлась пере­делкой программы roff Макиллроя (McIllroy), которую написал Д. Ф. Оссанна (J. F. Ossanna).

Семейство программ roff и TEX

В области систем форматирования документов доминируют два основных се­мейства программ: уходящие корнями в оригинальную программу roff, включая nroff и troff, и основанные на системе верстки Дональда Кнута TeX (произно­сится как «тек»). И да, буква «Е» в середине имени действительно смещена вниз. Имя roff произошло от словосочетания «run off» (напечатать), как во фразе: «Я напечатал копию для вас». Программа nroff используется для форматиро­вания документов перед выводом на устройства, использующие моноширинные шрифты, такие как алфавитно-цифровые терминалы и принтеры, действующие подобно пишущим машинкам. На момент появления программы такие устрой­ства составляли подавляющее большинство устройств вывода, подключаемых к компьютерам. Позднее появилась программа troff, форматирующая доку­менты для вывода на наборные устройства, используемые для производства «готовых к тиражированию» макетов. Большинство современных принтеров способны имитировать вывод таких наборных устройств. Семейство roff так­же включает ряд других программ для подготовки фрагментов документов К их числу относятся eqn (для форматирования математических формул) и tbl (для форматирования таблиц) Система TeX (в версии, готовой к эксплуатации) впервые появилась в 1989 году и, до определенной степени, заменила troff как инструмент для получения доку­ментов типографского качества. Мы не будем рассматривать систему TeX здесь, во-первых, из-за ее сложности (существуют целые книги, посвященные ей) и, во-вторых, из-за того, что в большинстве современных систем Linux она не устанав­ливается по умолчанию groff — это пакет программ с GNU-реализацией troff. Он также включает сцена­рий, имитирующий работу nroff, и остальные программы семейства roff Семейство roff и его потомки использовались для создания форматированных документов способом, довольно чуждым современным пользователям Большин­ство документов в наше время создается в текстовых процессорах, способных осуществлять составление и оформление документов в один шаг До появления графических текстовых процессоров создание документов обычно происходило в два этапа. Сначала в текстовом редакторе выполнялось составление документа, а затем с помощью процессора, такого как troff, осуществлялось его формати­рование Инструкции для программы форматирования встраивались в текст до­кумента с применением языка разметки Современным аналогом этого процесса может служить подготовка веб-страниц, которые записываются в текстовом ре­дакторе и затем отображаются веб-браузером, интерпретирующим код HTML как инструкции языка разметки, описывающие окончательный вид страницы Мы не будем рассматривать семейство программ groff во всех подробностях, так как многие тонкости его языка разметки имеют прямое отношение к туманным для нас деталям типографского дела Вместо этого мы сосредоточимся на одном из его макропакетов, широко использующемся до сих пор. Эти макропакеты упа­ковывают множество низкоуровневых команд в небольшое множество высоко­уровневых команд, существенно упрощающих работу с groff

Давайте ненадолго приостановимся и рассмотрим простую страницу справочно­го руководства (man). Она хранится в каталоге /usr/share/man в виде текстового файла, сжатого с помощью gzip. Если заглянуть на распакованное содержимое, можно увидеть следующее (здесь показана страница справочного руководства из раздела 1 для команды ls): Этот пример интересен тем, что страницы справочного руководства отображают­ся программой groff с использованием макропакета mandoc В действительности работу команды man можно сымитировать с помощью следующего конвейера Здесь использована программа groff с множеством параметров, определяющих макропакет mandoc и драйвер вывода для ASCII. groff может выводить информа­цию в нескольких форматах Если формат не задан, по умолчанию вывод произ­водится в формате PostScript:

PostScript — это язык описания страниц

PostScript — это язык описания страниц, используемый для вывода страниц на устройства печати с типографским качеством Вывод команды можно сохранить в файл (здесь предполагается, что вы работаете в графическом окружении рабоче­го стола и в вашем домашнем каталоге имеется каталог Desktop): После выполнения этой команды на рабочем столе появляется пиктограмма фай­ла После двойного щелчка мышью на этой пиктограмме должен запуститься ин­струмент просмотра страниц и отобразить содержимое файла (рис 21 1) Здесь мы видим прекрасно отформатированную страницу справочного руковод­ства для команды ls! В действительности PostScript-файл можно преобразовать в формат PDF (Portable Document Format — формат переносимых документов), как показано ниже: В последнем упражнении с программой groff мы вновь вернемся к нашему старо­му доброму другу — файлу distros. txt. На этот раз мы воспользуемся программой tbl, которая применяется для форматирования таблиц, чтобы сформировать наш список дистрибутивов Linux В этом упражнении мы задействуем сценарий для sed, созданный ранее, и с его помощью добавим разметку в поток текста, который затем передадим программе groff Сначала изменим сценарий для sed, добавив в него вызовы, необходимые про­грамме tbl. Откройте сценарий distros. sed в текстовом редакторе и измените его, как показано ниже: Обратите внимание, что для корректной работы сценария слова Name Version Released должны разделяться символами табуляции, а не пробелами. Сохраним получившийся файл с именем distros-tbl. sed. Программа tbl использует запросы. TS и. TE как метки начала и конца таблицы. Строки, следующие за запросом. TS, определяют глобальные свойства таблицы, в нашем случае выравнивание по цен­тру страницы и окружение внешней рамкой Остальные строки описывают фор­матирование строк таблицы Если теперь снова запустить наш конвейер составле­ния отчета с новым сценарием для sed, мы получим следующее: Параметр - t требует от groff обработать поток текста с помощью tbl. Аналогич­но, параметр - T требует вывести данные в формате ASCII вместо используемого по умолчанию PostScript Это лучший формат вывода для тех, кто ограничен возможностями терминала или алфавитноцифрового печатающего устройства Если определить формат вы­вода PostScript и открыть результат в графическом обозревателе, мы получим на­много более удовлетворительную картинку Учитывая, что текст занимает главенствующее положение в Unix-подобных операционных системах, было бы оправданно ожидать наличия большого числа ин­струментов для работы с текстом и его форматирования. И, как мы увидели, эти ожидания небеспочвенны! Простые инструменты форматирования, такие как fmt и pr, находят широкое применение в сценариях, производящих короткие доку­менты, тогда как groff (с сопутствующими программами) можно использовать для создания книг. Возможно, вам никогда не придется использовать инструмен­ты командной строки для написания технических статей (хотя многие делают это!), но знать о такой возможности вам не помешает. После знакомства с приемами манипулирования текстом в двух предыдущих гла­вах пришло время вывести его на бумагу. В этой главе мы рассмотрим инстру­менты командной строки, используемые для печати файлов и управления работой принтера. Мы не будем касаться вопросов настройки принтера, так как в разных дистрибутивах она осуществляется по-разному и обычно происходит автомати­чески в ходе установки. Для выполнения упражнений из этой главы вам понадо­бится настроенный и действующий принтер.

Краткая история поддержки печати

Для полного понимания особенностей печати в Unix-подобных операционных си­стемах сначала познакомимся с ее историей. Поддержка печати в Unix-подобных системах восходит к временам зарождения самой операционной системы В те времена принтеры и способы их использования существенно отличались от со­временных Подобно компьютерам, принтеры в эпоху, предшествующую появлению персо­нальных компьютеров, были большими, дорогими и централизованными. Ти­пичный пользователь 1980-х работал за терминалом на порядочном удалении от ЭВМ. Принтер же размещался рядом с ЭВМ и находился под неусыпным наблю­дением операторов Когда принтеры были дорогими и централизованными, что было обычно в ранний период развития Unix, нормальной практикой считалось совместное использова­ние принтера несколькими пользователями. Чтобы идентифицировать принад­лежность задания печати определенному пользователю, в начале каждого задания печаталась титульная страница с именем пользователя Персонал, обслуживаю­щий ЭВМ, извлекал корзину с напечатанными за день документами и доставлял их конкретным пользователям Технология печати в 80-х существенно отличалась от современной двумя аспекта­ми. Во-первых, практически все принтеры того периода были ударного действия. В этих принтерах использовался механический узел, ударявший по красящей ленте, находящейся между узлом и бумагой и таким способом формировавший отпечатки символов на бумаге В то время были наиболее распространены два вида печатающих механизмов: лепестковый литероноситель («ромашка») и ма­тричная печатающая головка Во-вторых, что особенно важно, ранние принтеры использовали фиксированный набор символов, встроенный непосредственно в устройство. Например, принтер с «ромашкой» мог печатать только символы, присутствующие на лепестках «ромашки». В результате такие принтеры действовали как высокоскоростные печа­тающие машинки Как большинство печатающих машинок, они печатали моно­ширинными шрифтами (шрифтами с фиксированной шириной символов) Это означает, что все символы имели одинаковую ширину Печать символов выпол­нялась в фиксированных позициях на бумаге, и область печати имела фиксиро­ванное число знакомест. Большинство принтеров выводило 10 символов на дюйм (Characters Per Inch, CPI) по горизонтали и 6 строк на дюйм (Lines Per Inch, LPI) по вертикали. В соответствии с этой схемой на странице формата US-letter поме­щалось 85 символов в ширину и 66 строк в высоту. С учетом отступов слева и спра­ва считалось, что максимальная ширина печати составляет 80 символов в строке Это объясняет, почему дисплеи терминалов (и наших эмуляторов терминалов) обычно имеют ширину 80 символов Таким способом обеспечивался режим «что вижу, то и получаю» (What You See Is What You Get, WYSIWYG) отображения текста перед печатью с применением моноширинного шрифта Данные, посылаемые на принтер с ударным механизмом, — это простой поток байтов, соответствующих печатаемым символам Например, чтобы напечатать a, посылался код символа 97 в наборе ASCII. Кроме того, в начале набора ASCII были выделены коды для управления кареткой принтера и бумагой, например, для возврата каретки в исходное положение, перевода строки, перевода формата (страницы) и др. С помощью управляющих кодов можно было имитировать неко­торые эффекты, такие как жирная печать, когда принтеру посылался печатаемый символ, символ забоя (backspace) и снова тот же печатаемый символ, благодаря чему на бумаге получалось более темное изображение В этом легко убедиться, если с помощью nroff отобразить страницу справочного руководства и исследо­вать вывод с помощью команды Символы AH (CTRL-H) — это символы забоя (backspace), используемые для созда­ния эффекта жирной печати. Аналогично для получения эффекта подчеркивания можно использовать последовательность из символов забоя/подчеркивания.

Графические принтеры

Создание графического интерфейса пользователя (GUI) привело к существен­ным изменениям в технологии печати. Так же как компьютеры все больше смеща­лись в сторону использования графических дисплеев, печать все дальше уходила от символьных технологий вывода к графическим. Эта задача упростилась с появ­лением недорогих лазерных принтеров, которые вместо фиксированного набора символов могли осуществлять печать маленькими точками в любом месте обла­сти печати на странице Это позволило использовать для печати пропорциональ­ные шрифты (подобные тем, что применяются в книгопечатании) и даже печатать фотографии и высококачественные диаграмм Однако переход от символьной системы печати к графической вызвал появление огромных технических проблем. И вот почему. Число байтов, которое нужно по­слать символьному принтеру для заполнения страницы, можно было подсчитать с помощью простой формулы (если предположить, что на странице умещается 60 строк, по 80 символов в каждой): 60 X 80 = 4800 байт. Для сравнения: лазерному принтеру с качеством печати 300 точек на дюйм (Dot Per Inch, DPI) для покрытия страницы размером 8 х 10 дюймов (203 х 254 мм) нужно послать (8 X 300) X (10 X 300) X 8 = 900 000 байт Многие медленные сети персональных компьютеров просто не могли достаточно быстро пропустить почти 1 мегабайт данных, чтобы напечатать на лазерном прин­тере полную страницу, поэтому требовалось какое-то новое решение Таким решением стало изобретение языка описания страниц. Язык описания страниц (Page Description Language, PDL) — это язык программирования, описывающий содержимое страницы. Программы на этом языке как бы говори­ли: «перейти в эту позицию, нарисовать символ a шрифтом Helvetica с кеглем 10 пунктов, перейти в эту позицию. . .», пока вся страница не была описана. Пер­вым основным языком PDL стал PostScript, разработанный в Adobe Systems, он все еще широко используется в наше время. Язык PostScript — это полноценный язык программирования, ориентированный на книгопечатание и создание раз­ного вида графических изображений. Он включает поддержку 35 стандартных высококачественных шрифтов плюс может принимать определения дополни­тельных шрифтов во время выполнения. На первом этапе поддержка PostScript встраивалась непосредственно в принтеры. Это решало проблему передачи дан­ных. Даже при том, что типичная программа на PostScript по объему превышала простой поток байтов для символьных принтеров, ее размер был намного меньше числа байтов, необходимых для представления целой страницыПринтер с поддержкой PostScript принимал на входе программу на PostScript. Принтер имел собственный процессор и память (нередко принтеры имели боль­шую вычислительную мощность, чем компьютеры, к которым они подключались) и выполнял специальную программу, называвшуюся интерпретатором PostScript, которая читала входящую программу на PostScript и отображала результат во вну­треннюю память принтера, таким образом формируя шаблон из битов (точек) для вывода на бумагу Такой процесс отображения чего-то в большой битовый шаблон (его называют bitmap — растр) в общем случае называют процессором растровых изображений (Raster Image Processor, RIP) . Спустя годы компьютеры и сети стали намного быстрее Это позволило переме­стить RIP с принтера в компьютер, что, в свою очередь, позволило удешевить вы­сококачественные принтеры Многие современные принтеры все еще способны принимать потоки символов, но большинство уже не поддерживают эту возможность Они полагаются на RIP компьютера и ожидают получить поток битов для печати их в виде точек Суще­ствуют также современные PostScript-принтеры.

Печать в Linux

Современные системы Linux используют два комплекта программного обеспече­ния для печати. Первый, CUPS (Common Unix Printing System — общая система печати для Unix), включает драйверы печати и средства управления заданиями; второй, Ghostscript, интерпретатор PostScript, действует как RIPПакет CUPS осуществляет управление принтерами, создавая очереди печати и управляя ими. Как говорилось выше, в краткой исторической справке, под­держка печати в Unix первоначально была ориентирована на управление центра­лизованным принтером, совместно используемым несколькими пользователями Поскольку принтеры — довольно медленные устройства по своей природе в срав­нении с компьютерами, которые поставляют им данные, системы печати должны обладать возможностью планирования множества заданий печати и их организа­ции Система CUPS также способна распознавать данные разных типов (в разум­ных пределах) и преобразовывать файлы в вид, пригодный для печати Так как мы — пользователи командной строки, наибольший интерес для нас пред­ставляет печать текста, хотя при этом сохраняется возможность печатать данные других форматов Мы уже касались программы pr в предыдущей главе. А теперь исследуем все бо­гатство ее параметров, используемых при печати В краткой исторической справке развития технологий печати рассказывалось, что символьные принтеры исполь­зовали мноноширинные шрифты, что обеспечивало фиксированное число симво­лов в строке и строк на странице Программа pr используется для выравнивания текста в соответствии с заданным размером страницы, с учетом дополнительных заголовков и полей на странице. Наиболее часто используемые параметры пере­числены Пакет программ печати CUPS поддерживает два метода печати, исторически ис­пользуемых в Unix-подобных системах. Первый метод, с названием Berkeley, или LPD (используется в BSD-версиях Unix), основан на использовании програм­мы lpr; второй метод, с названием SysV (используется в версиях Unix System V), основан на использовании программы lp Обе программы работают примерно одинаково Выбор той или иной зависит от личных предпочтений Программа lpr применяется для отправки файлов на принтер Она также может использоваться в конвейерах, так как способна принимать исходные данные со стандартного ввода Например, напечатать предыдущий результат форматирова­ния содержимого каталога в несколько Подобно lpr, программа lp принимает файлы или данные со стандартного ввода Отличается от lpr поддержкой иного (немного более сложного) набора парамет­ров. Наиболее часто используемые параметры перечислены

Вернемся к нашему списку содержимого каталога, но на этот раз выведем его с плотностью печати 12 CPI и 8 LPI и с левым полем размером полдюйма. Обра­тите внимание, что нам пришлось откорректировать параметры pr, чтобы учесть новые размеры страницы: Этот конвейер выводит список в четыре колонки с меньшим размером шрифта, чем принято по умолчанию. Увеличение плотности символов на дюйм позволило уместить больше колонок на странице Программа a2ps довольно интересна. Как можно догадаться по ее имени, это программа преобразования одного формата в другой, но не только Первона­чально ее имя означало ASCII to PostScript (из ASCII в PostScript) и она исполь­зовалась для подготовки текстовых файлов к печати на принтерах с поддержкой PostScript С годами, однако, возможности программы росли, и теперь ее имя означает Anything to PostScript (все что угодно — в PostScript) . Несмотря на то что имя программы говорит, что это — программа преобразования одного фор­мата в другой, в действительности она является программой печати Она выво­дит результаты своей работы в свой вывод по умолчанию — в системный принтер, а не в стандартный вывод По умолчанию программа действует как программа «структурной печати», улучшая формат вывода Мы можем с ее помощью создать PostScript-файл на своем рабочем столе: Здесь мы обработали поток с помощью программы pr, передав ей параметр - t (чтобы опустить верхние и нижние колонтитулы), и передали результат програм­ме a2ps, указав ей файл для вывода (параметр - o) и плотность печати 66 строк на странице (параметр - L), чтобы разбить вывод pr на страницы. Если открыть получившийся файл с помощью соответствующего средства просмотра, можно увидеть, что он выглядит.

Наблюдение за заданиями печати и управление ими

Поскольку система печати в Unix изначально проектировалась для обработки заданий печати от нескольких пользователей, соответственно и система CUPS проектировалась исходя из той же предпосылки. Для каждого принтера созда­ется своя очередь печати, в которой задания хранятся, пока не будут переданы принтеру В составе CUPS имеется несколько программ командной строки для управления состоянием принтеров и очередей печати Подобно программам lpr и lp, эти управляющие программы создавались после появления соответствую­щих программ из систем Berkeley и System V. Программу lpstat удобно использовать для определения имен и доступности принтеров в системе. Например, если к системе подключены два принтера — фи­зический (с именем printer) и виртуальный, для вывода в файлы PDF (с именем PDF), — их состояние можно проверить так:

Кроме того, с ее помощью можно получить более подробное описание конфигура­ции системы печати: device for printer: ipp://print-server:631/printers/printer В этом примере видно, что имя printer соответствует системному принтеру по умолчанию и что это сетевой принтер, для взаимодействий с которым использует­ся протокол печати через Интернет (Internet Printing Protocol, ipp://), физически подключенный к системе с именем print-server. Программа lpq используется для получения информации о состоянии очереди печати. С ее помощью можно увидеть состояние очереди и список заданий в ней. Ниже приводится пример вывода информации о состоянии пустой очереди для системного принтера по умолчанию с именем printer. Если принтер не указан (с помощью параметра - P), выводится информация об очереди для системного принтера по умолчанию Если сформировать задание для печати и затем вывести информацию о состоянии очереди, это задание появится в списке: В составе CUPS имеется две программы для завершения заданий печати и удале­ния их из очереди. Одна программа — в стиле Berkeley (lprm), а другая в стиле System V (cancel) . Они несколько отличаются поддерживаемыми параметрами, но, по сути, выполняют одну и ту же операцию Если использовать пример с за­данием печати, рассматриваемый выше, мы могли бы остановить выполнение за­дания и удалить его:

Обе команды имеют параметры, позволяющие удалить все задания, принадлежа­щие определенному пользователю задания, предназначенные для печати на опре­деленном принтере, а также задания, содержащиеся в указанном списке номеров заданий Все необходимые подробности вы найдете на страницах справочного ру­ководства (man) для этих команд В этой главе мы посмотрим, как собирать программы, компилируя их исходный код. Доступность исходного кода — основное преимущество Linux, оно обеспечи­вает само существование этой системы. Вся экосистема разработки в Linux опира­ется на свободный обмен информацией между разработчиками Для многих рядо­вых пользователей компиляция — утраченное искусство. Когда-то эта процедура была вполне обыденным делом, но в настоящее время создатели дистрибутивов поддерживают огромные репозитории с предварительно скомпилированными файлами, готовыми для загрузки и использования На момент написания этих строк в репозитории дистрибутива Debian (одном из крупнейших) насчитывалось почти 23 000 пакетов. Но зачем может понадобиться компилировать исходный код? Могу назвать две основные причины: Доступность. Несмотря на большое число предварительно скомпилированных пакетов в репозиториях дистрибутивов некоторые дистрибутивы могут вклю­чать не все необходимые приложения В этом случае остается только один спо­соб установить требуемую программу: скомпилировать ее из исходных кодов Своевременность. Даже при том, что некоторые дистрибутивы специализиру­ются на ультрасовременных версиях программ, многие все же немного отстают от прогресса. Это означает, что для получения самой последней версии про­граммы придется ее скомпилировать

Что такое компиляция?

Выражаясь простым языком, компиляция — это процесс трансляции исходного кода (текста программы, описывающего ее действия и написанного программи­стом) на низкоуровневый язык, понятный процессору компьютера. Компиляция программ из исходных кодов может оказаться весьма специфиче­ским и технически сложным делом, непосильным для некоторых пользователей Однако многие программы компилируются относительно легко и просто, всего в несколько шагов. Все зависит от пакета. Далее мы рассмотрим очень простой случай, чтобы получить общее понимание процесса и начальные знания, оттал­киваясь от которых желающие смогут продолжить исследования самостоятельно. В этой главе будет представлена одна новая команда: О make — утилита сопровождения программ. Процессор компьютера (Computer Processor Unit, CPU) работает на очень низ­ком уровне, выполняя программы на языке, который называют машинным. Это числовой код, описывающий элементарные операции, такие как «сложить эти два байта», «сослаться на эту ячейку в памяти» или «скопировать этот байт». Каждая из этих инструкций выражается в двоичной форме (нулями и единицами) Самые первые программы писались на числовом коде, поэтому программисты, писавшие такой код, как поговаривают, много курили, пили кофе литрами и носили очки с толстенными линзами. Эта проблема была решена с появлением языка ассемблера, который заменил чис­ловые коды (слегка) более простыми символическими мнемониками, такими как CPY (для обозначения операции копирования) и MOV (для обозначения операции перемещения) . Исходный код на языке ассемблера преобразовывался в машин­ный код программой, называющейся ассемблером. Язык ассемблера используется и в наши дни для решения специальных задач программирования, таких как раз­работка драйверов устройств или встраиваемых систем Затем появились высокоуровневые языки программирования Они называются так потому, что позволяют программисту меньше думать об особенностях работы процессора и больше — о решении задачи, стоящей перед ним К числу этих пер­вых языков (разработанных в течение 1950-х) относятся: FORTRAN (создавался для решения научных и технических задач) и СOBOL (для решения экономиче­ских задач) Оба продолжают ограниченно использоваться и по сию пору Несмотря на большое число популярных языков программирования, господству­ющие позиции занимают только два из них Многие современные системы и про­граммы написаны на C или на C++. В примерах ниже мы будем компилировать программы на языке C

Программы на языках высокого уровня преобразуются в инструкции на машин­ном языке с помощью другой программы — компилятора. Некоторые компилято­ры транслируют высокоуровневые инструкции на язык ассемблера, а затем с по­мощью ассемблера производят окончательную трансляцию на машинный язык Компиляции, как правило, сопутствует процесс компоновки. Программы часто вы­полняют множество типовых операций Возьмем для примера операцию откры­тия файла. Многие программы выполняют ее, но было бы слишком расточительно в каждой программе реализовывать свою процедуру открытия файлов. Предпо­чтительнее иметь единый программный код, знающий, как открывать файлы, и дать всем программам возможность использовать его. Поддержка решения типовых задач осуществляется с помощью библиотек. Они содержат множество подпрограмм, которые решают типовые задачи и могут использоваться множе­ством программ. Если заглянуть в каталоги /lib и /usr/lib, мы обнаружим подоб­ные библиотеки Для формирования связей между результатом работы компиля­тора и библиотеками, необходимыми компилируемой программе, используется программа-компоновщик (linker, ее также называют редактором связей) . Окон­чательным результатом этого процесса является выполняемый файл программы, готовый к использованию

Все ли программы компилируются?

Нет Как мы уже видели, некоторые программы, такие как сценарии на языке командной оболочки, не требуют компиляции и выполняются непосредственно Они написаны на языках, которые называют языками сценариев, или интерпре­тируемыми языками. К числу этих языков, популярность которых только растет в последние годы, относятся Perl, Python, PHP, Ruby и многие другие. Программы на языках сценариев выполняются специальной программой, интер­претатором Интерпретатор получает файл программы, читает его и выполняет каждую инструкцию, содержащуюся в нем Вообще, интерпретируемые програм­мы выполняются намного медленнее, чем компилируемые. Это объясняется необходимостью транслировать исходный код каждой инструкции в интерпретиру­емой программе всякий раз, когда она встречается, тогда как в скомпилированной программе исходный код инструкций был уже однажды преобразован в машин­ный код и сохранен в окончательном выполняемом файле Но почему тогда интерпретирующие языки так популярны? Для многих рутинных задач они оказываются «достаточно быстрыми», но самое большое их достоин­ство в простоте и скорости разработки интерпретируемых программ в сравнении с компилируемыми. Разработка программ — это обычно циклический процесс, включающий три этапа: создание исходного кода, компиляцию и тестирование С увеличением программы в размерах этап компиляции в упомянутом цикле мо­жет оказаться весьма продолжительным Интерпретирующие языки избавляют от необходимости компиляции и тем самым ускоряют их разработку Давайте что-нибудь скомпилируем Для этого нам понадобятся некоторые ин­струменты, такие как компилятор, компоновщик и утилита make Практически во всех системах Linux используется один и тот же компилятор языка C с именем gcc (GNU C Compiler), первоначально написанный Ричардом Столлманом. Многие дистрибутивы не устанавливают gcc по умолчанию Проверить его присутствие в системе можно так: В нашем упражнении мы скомпилируем программу с названием diction из про­екта GNU. Эта маленькая удобная программка проверяет качество и стиль содер­жимого текстовых файлов. А поскольку она невелика, она легко компилируется. Следуя соглашениям, мы сначала создадим каталог src для исходного кода и затем загрузим в него исходный код с помощью команды ftp: Исходный код обычно распространяется в виде сжатого tar-файла. Иногда назы­ваемые тарболлами (tarball), эти файлы содержат дерево исходных текстов, или иерархию каталогов и файлов, составляющих исходный код Подключившись к FTP-сайту, мы получили список доступных tar-файлов и выбрали для загруз­ки самую свежую версию. При помощи команды get программы ftp скопировали файл с сервера FTP на локальную машину После загрузки tar-файла его нужно распаковать. Делается это с помощью про­граммы tar: В результате распаковывания tar-файла был создан новый каталог new diction-1.11 . Этот каталог содержит дерево исходных текстов Давайте заглянем внутрь: Здесь мы видим множество файлов. Программы, принадлежащие проекту GNU, а также многие другие поставляются вместе с файлами документации README, INSTALL, NEWS и COPYING. В них содержится описание программы, информа­ция о порядке сборки и установки и условия лицензионного соглашения. Я реко­мендую всегда внимательно прочитывать файлы README и INSTALL перед сбор­кой программы Другими интересными файлами в этом каталоге являются файлы с расширения­ми. c и. h: Файлы с расширением. c содержат две программы на C, входящие в состав пакета (style и diction), разбитые на несколько модулей. Большие программы часто раз­бивают на более мелкие, более простые в сопровождении фрагменты Файлы с ис­ходным кодом содержат простой текст, и их можно исследовать с помощью less:

Файлы с расширением. h известны как заголовочные файлы. Они тоже содержат простой текст — описание подпрограмм, подключаемых из файла с исходным ко­дом или библиотеки. Чтобы компилятор смог связать модули, он должен иметь описание всех модулей, составляющих единую программу. Ближе к началу в фай­ле diction. c имеется следующая строка:

Сборка программ

В большинстве случаев сборка программы заключается в выполнении последова­тельности из двух команд: Программа configure — это сценарий командной оболочки, поставляемый вместе с деревом исходных текстов. Его задача — проанализировать окружение сборки. Большинство исходного кода поддерживает переносимость. То есть такой исход­ный код спроектирован так, что допускает сборку в разных Unix-подобных си­стемах Но для этого во время сборки в исходный код может потребоваться вне­сти небольшие изменения, учитывающие различия между системами Программа configure также проверяет наличие необходимых внешних инструментов и компо­нентов Она требует от компилятора прочитать фал getopt. h, прежде чем продолжать чтение исходного кода в diction. c, чтобы «узнать», что имеется в файле getopt. c. Файл getopt. c содержит код подпрограмм, используемых обеими программами, style и diction Инструкции подключения файла getopt. h предшествует еще несколько инструк­ций include: Они также ссылаются на заголовочные файлы, но эти файлы хранятся за предела­ми дерева исходных текстов Они должны поставляться системой для поддержки компиляции программ. Эти файлы можно найти в каталоге /usr/include: Заголовочные файлы помещаются в этот каталог в процессе установки компиля­тора Давайте запустим configure. Так как эта программа находится не там, где команд­ная оболочка обычно ищет выполняемые файлы, нужно явно сообщить ей место­положение программы, добавив в команду префикс./ . Он указывает, что програм­ма находится в текущем рабочем каталоге: В процессе проверки и настройки сборки configure выведет множество сообще­ний Последние строки ее вывода должны выглядеть примерно так: Самое важное здесь — отсутствие сообщений об ошибках. Их появление означало бы неудачу настройки и невозможность сборки программы до их устранения Как мы видим, configure создала в каталоге с исходным кодом несколько файлов. Самым важным является Makefile. Файл Makefile — это конфигурационный файл с инструкциями для программы make, описывающими, как собрать программу. Без такого файла утилита make работать не будет. Makefile — обычный текстовый файл, то есть мы можем заглянуть в него: Программа make принимает файл сборки (обычно с именем Makefile), в котором описываются отношения и зависимости между компонентами, составляющими окончательную программу Первый раздел в файле сборки определяет переменные для подстановки в после­дующих его разделах. Например, здесь можно увидеть строку пределяющую, что роль компилятора C будет играть gcc Далее в файле можно посмотреть, как используется это определение: Большую часть файла сборки занимают строки, определяющие целевой файл (target) — в данном случае выполняемый файл diction — и файлы, от которых она зависит Остальные строки описывают команды, которые необходимо выпол­нить для создания целевого файла из его компонентов. Мы видим, что выполня­емый файл diction (одна из конечных программ) зависит от присутствия файлов diction. o, sentence. o, misc. o, getopt. o и getopt1.o. Далее в файле сборки присутствуют определения, в которых каждый из этих файлов играет роль целевого Однако в этих определениях не видно ни одной команды. Обработка этих строк осуществляется определением общей цели, что находится выше в файле, где описывается команда компиляции всех файлов с расширением. c в файлы с расшире­нием. o: На первый взгляд все это кажется очень сложным. Почему бы просто не перечис­лить все этапы компиляции? Ответ на этот вопрос станет очевиден чуть позже. А пока давайте запустим make и соберем наши программы: Программа make запустится и выполнит все инструкции в файле Makefile. В про­цессе работы она выведет множество сообщений А по завершении мы увидим, что в каталоге появились все целевые файлы: Среди них diction и style, программы, которые мы намеревались собрать. Примите заслуженные поздравления! Мы только что скомпилировали первые программы из исходного кода! Она вывела довольно странное сообщение Но почему? Почему она не выполни­ла сборку программы повторно? Во всем виновата make Вместо того чтобы про­сто собрать все заново, make собирает только то, что нужно собрать. Так как все целевые файлы уже присутствуют в каталоге, make решила, что ничего больше делать не требуется Продемонстрировать это можно, удалив одну из собранных целей и запустив make снова Вы увидите, что make повторно собирает getopt. o и заново компонует программы diction и style, потому что они зависят от отсутствующего модуля Такое пове­дение указывает на еще одну важную особенность make: она старается обеспечить актуальность целевых файлов make гарантирует, что целевые файлы будут более новыми, чем их зависимости В этом есть определенный смысл, потому что про­граммист часто сначала изменяет исходный код, а затем запускает make, чтобы со­брать новую версию программы make гарантирует сборку всех целевых файлов, опирающихся на изменившийся код Воспользуемся программой touch, чтобы «обновить» один из файлов с исходным кодом, и посмотрим, к чему это приведет

Создание первого сценария командной оболочки

В предыдущих главах мы собрали арсенал инструментов командной строки. Не­смотря на то что эти инструменты можно использовать для решения самых разных вычислительных задач, мы все еще вынуждены использовать их по старинке, по одному. Было бы замечательно переложить еще больше ручной работы на команд­ную оболочку. Оказывается, это возможно. Объединяя инструменты в программы собственной конструкции, мы можем заставить командную оболочку самостоя­тельно выполнять сложные последовательности операций Такие программы на­зываются сценариями командной оболочки. Выражаясь простым языком, сценарий командной оболочки — это файл, содержа­щий последовательность команд. Командная оболочка читает этот файл и выпол­няет команды, как если бы они вводились вручную в командной строке Командная оболочка — это одновременно и мощный интерфейс командной стро­ки к системе, и интерпретатор языка сценариев Как вы увидите далее, многое из того, что можно сделать в командной строке, также можно сделать в сценариях, а многое из того, что можно сделать в сценариях, можно сделать в командной строке Мы уже познакомились с множеством особенностей командной оболочки, но все внимание уделялось только особенностям, связанным с непосредственным использованием командной строки Но командная оболочка Способность программы make выполнять сборку только целей, которые действи­тельно этого требуют, дает программистам немалые выгоды Экономия времени, возможно, не очевидна для нашего маленького проекта, но она намного заметнее в больших проектах Вспомните, например, что ядро Linux (программа, кото­рая постоянно изменяется и совершенствуется) содержит несколько миллионов строк кода Старательно упакованный исходный код часто включает специальную цель для make, которая называется install (установить) Эта цель выполняет установку готового программного продукта в системный каталог Обычно это каталог /usr/ local/bin, традиционное место для установки программного обеспечения, собран­ного в локальной системе Однако этот каталог, как правило, недоступен рядовым пользователям для записи, поэтому, чтобы выполнить установку, вам потребуют­ся привилегии суперпользователя: В этой главе мы узнали, как с помощью трех простых команд — ./configure, make, make install — собирать пакеты из исходных кодов Кроме того, мы увидели, на­сколько важную роль играет make в сопровождении программ Программу make можно использовать для решения многих задач, где требуется поддерживать от­ношения цель/зависимость, а не только для компиляции исходного кода. обладает также множеством особенностей, обычно (но не всегда) используемых при создании программ Чтобы успешно создать и запустить сценарий командной оболочки, нам нужно: Написать сценарий. Сценарии командной оболочки — это обычные текстовые файлы. Поэтому для их создания нам понадобится текстовый редактор. Лучше использовать текстовый редактор, обладающий функцией подсветки синтак­сиса, позволяющей видеть элементы сценариев с цветной маркировкой Под­светка синтаксиса помогает замечать некоторые типичные ошибки Для со­здания сценариев хорошо подходят vim, gedit, kate и многие другие редакторы. Сделать сценарий выполняемым. Система не позволяет интерпретировать любой старый текстовый файл как программу, и небезосновательно! Поэтому, чтобы выполнить сценарий, файлу сценария нужно дать разрешения на вы­полнение Поместить сценарий в каталог, где командная оболочка сможет найти его. Командная оболочка автоматически выполняет поиск выполняемых файлов в нескольких каталогах, если путь к файлу не указан явно. Для максимального удобства мы будем помещать наши сценарии в такие каталоги

Формат файла сценария

Следуя традициям программирования, напишем программу «hello world», чтобы продемонстрировать чрезвычайно простой сценарий Итак, запустите текстовый редактор и введите следующий сценарий: Это наш первый сценарий. echo 'Hello World!' Последняя строка в сценарии хорошо знакома — это простая команда echo со строковым аргументом Вторая строка также знакома Она выглядит как коммен­тарии, которые мы видели во многих конфигурационных файлах, исследованных и отредактированных нами Еще одна особенность комментариев, о которой пока не рассказывалось, — они могут появляться в концах строк, например: Hello World! Несмотря на бессмысленность комментариев в командной строке, их все же мож­но использовать Первая строка в сценарии смотрится несколько необычно. Она похожа на ком­ментарий, потому что начинается с символа #, но выглядит какой-то уж слишком специальной, чтобы быть комментарием. Последовательность символов #! — это на самом деле специальная конструкция, которая называется shebang (произ­носится как «ше-банг») и сообщает системе имя интерпретатора, который дол­жен использоваться для выполнения следующего за ним текста сценария Каждый сценарий командной оболочки должен включать это определение в первой строке Сохраните файл сценария с именем hello_world. Далее сделаем сценарий исполняемым при помощи команды chmod: Существует два распространенных набора разрешений для сценариев: 755 — для сценариев, которые должны быть доступны для выполнения всем, и 700 — для сценариев, которые могут выполняться только владельцами Обратите внимание, что сценарии необходимо сделать доступными для чтения, чтобы их можно было выполнить После установки разрешений попробуем запустить сценарий: В чем причина? Чем наш сценарий отличается от других программ? Как оказы­вается, ничем. У нас замечательный сценарий. Его проблема — местоположение. В главе 11 мы обсуждали переменную окружения PATH и ее влияние на то, как система ищет выполняемые программы Коротко напомним, что система просма­тривает каталоги по списку всякий раз, когда требуется найти исполняемую про­грамму, если путь к ней не указан явно Именно так система выполняет программу /bin/ls, если мы вводим ls в командной строке Каталог /bin — один из каталогов, которые система просматривает автоматически Список каталогов хранится в пе­ременной окружения PATH Она содержит список каталогов, перечисленных через двоеточие. Увидеть, что содержится в PATH, можно с помощью команды: Как видите, это просто список каталогов Если поместить сценарий в любой из этих каталогов, наша проблема будет решена Обратите внимание на первый ка­талог в списке, /home/me/bin. В большинстве дистрибутивов Linux в переменную PATH включается каталог bin в домашнем каталоге пользователя, чтобы дать поль­зователям возможность выполнять собственные программы То есть если создать каталог bin и поместить сценарий в него, его можно будет запускать как любые другие программы: Это изменение будет действовать в каждом последующем сеансе работы с терми­налом Чтобы применить изменения в текущем сеансе, нужно заставить команд­ную оболочку повторно прочитать файл. bashrc, например, так: Команда «точка» (. ) является синонимом source, встроенной команды, которая читает указанный файл и интерпретирует его как ввод с клавиатуры Ubuntu автоматически добавляет каталог ~/bin в переменную PATH, если он существует в момент выполнения файла. bashrc. То есть если в системе Ubuntu создать каталог ~/ bin и затем выйти и войти в систему, проблема решится автоматически.

Выбор местоположения для сценариев

Каталог ~/bin хорошо подходит для сценария, если этот сценарий предназначен для личного использования Сценарии, которые должны быть доступны всем пользователям в системе, лучше размещать в традиционном местоположении — в каталоге /usr/local/bin. Сценарии, предназначенные для использования си­стемным администратором, часто помещаются в каталог /usr/local/sbin В боль­шинстве случаев программное обеспечение, созданное в локальной системе, будь то сценарии или скомпилированные программы, следует помещать в иерархию каталогов /usr/local, а не /bin или /usr/bin Последние два каталога, как опреде­лено стандартом иерархии файловой системы Linux (Linux Filesystem Hierarchy Standard), предназначены только для файлов, поставляемых создателями дистрибутива Linux Одной из ключевых целей, стоящих перед создателями сценариев, является про­стота сопровождения, то есть простота, с какой сценарий может быть изменен автором или другими пользователями для удовлетворения меняющихся потреб­ностей Один из способов упростить сопровождение — улучшить читаемость и по­нятность сценария Многие команды, с которыми мы уже знакомы, поддерживают параметры с ко­роткими и длинными именами Например, команда ls имеет множество параме­тров, которые можно выразить в короткой и в длинной форме Например:это эквивалентные команды Параметры с короткими именами предпочтительнее использовать в командной строке, так как они помогают уменьшить ручной ввод, но длинные имена параметров могут улучшить читаемость Если приходится использовать длинные команды, их читаемость можно повы­сить, распределяя такие команды по нескольким строкам. В главе 17 был пред­ставлен пример длинной команды С помощью последовательностей продолжения строки (включающих обратный слеш и символ перевода строки) и отступов логику этой сложной команды уда­лось сделать ясной для читателя Этот прием работает также в командной строке, однако он редко используется из-за неудобства ввода и редактирования Одно из отличий сценариев о командной строки — возможность использования символов табуляции для оформления отступов, тогда как в командной строке это невозмож­но, потому что клавиша ввода табуляции активирует функцию автодополнения Текстовый редактор vim имеет много, очень много параметров настройки. Некоторые из них можно использовать для подготовки редактора к разработке сценариев. :syntax on включает подсветку синтаксиса. С этой настройкой редактор будет ото­бражать синтаксические элементы сценариев разным цветом. Это помогает выявлять некоторые виды ошибок. И конечно же, выглядит очень круто. Обратите внимание, что для работы этой настройки должна быть установлена полная версия vim, а редак­тируемый файл должен содержать строку шебанг (shebang), сообщающую, что файл является сценарием командной оболочки. Если с настройкой :syntax on возникнут сложности, попробуйте настройку :set syntax=sh. :set hlsearch включает подсветку результатов поиска. Например, если выполнить поиск слова echo, с этой настройкой редактор выделит все вхождения искомого слова. :set tabstop=4 определяет число колонок (знакомест), занимаемых символом табуля­ции. По умолчанию один символ табуляции занимает восемь знакомест. Присвоив этому параметру значение 4 (которое широко используется практикующими программистами), вам проще будет уместить длинные строки на экране. :set autoindent включает автоматическое оформление отступов. Этот параметр за­ставляет vim добавлять в новую строку отступ, как в строке выше. Это ускоряет ввод многих видов программных конструкций. Чтобы прекратить автоматическое выравни­вание, достаточно нажать комбинацию CTRL+D. Эти изменения можно сделать постоянными, добавив описанные команды (без началь­ного символа двоеточия) в файл ~/.vimrc. В первой главе, посвященной сценариям, мы увидели, как писать сценарии и как упростить их запуск в своей системе. Мы также познакомились с некоторыми приемами оформления, улучшающими читаемость (и тем самым упрощающими сопровождение) сценариев В следующих главах мы снова и снова будем возвра­щаться к простоте сопровождения как главному принципу создания качествен­ных сценариев

Создание программы

В этой главе мы приступаем к созданию программы. Цель данного проекта — по­казать, как можно использовать разные возможности командной оболочки для создания программ и, что особенно важно, для создания хороших программ. Далее мы напишем генератор отчетов. Он будет выводить разнообразную ин­формацию о системе и ее состоянии в формате HTML, благодаря чему ее можно будет просматривать в веб-браузере Обычно создание программ выполняется в несколько этапов, на каждом из ко­торых добавляются новые функции и возможности По окончании первого этапа наша программа будет воспроизводить минимальную HTML-страницу без какой - либо информации. Эту информацию мы добавим на следующих этапах. Прежде всего, определим, как выглядит формат правильно сформированного HTML-документа. Он имеет следующий вид: Если ввести этот текст в текстовом редакторе и сохранить в файле с именем foo. html, мы сможем открыть его, введя следующий адрес URL в Firefox: file:///home/ username/foo. html. На первом этапе создадим программу, которая будет выводить эту разметку HTML в стандартный вывод. Написать такую программу очень просто. Откройте текстовый редактор и создайте файл с именем ~/bin/sys_info_page: Наша первая версия содержит строку-шебанг (shebang), комментарий (можно только приветствовать) и последовательность команд echo, по одной для вывода каждой строки После сохранения файла сделайте его выполняемым и попробуй­те запустить: После запуска на экране должен появиться текст HTML-документа, потому что команды echo в сценарии посылают свои строки в стандартный вывод. Запусти­те программу снова и перенаправьте вывод программы в файл sys_info_page. html, чтобы затем посмотреть результат в веб-браузере: Пока все идет неплохо Разрабатывая программы, всегда следует помнить о простоте и ясности. Сопрово­ждение дается проще, когда программа легко читается и доступна для понимания, не говоря уже о том, что программу легче писать, когда есть возможность умень­шить объем ручного ввода. Текущая версия программы работает замечательно, но ее можно упростить. Если объединить все команды echo в одну, это определенно упростит в будущем добавление новых строк в вывод программы Поэтому изме­ним программу, как показано ниже: Строки в кавычках могут включать символы перевода строки и, соответственно, содержать несколько строк текста. Командная оболочка будет продолжать читать текст, пока не встретит закрывающую кавычку Это правило действует также в ко­мандной строке: Символ > в начале каждой строки — это приглашение к вводу командной обо­лочки, определяемое ее переменной PS2. Оно появляется всякий раз, когда про­исходит ввод многострочной инструкции Эта особенность пока мало понятна, но потом, когда мы познакомимся с многострочными программными инструкциями, ее преимущества станут очевидными Теперь, когда программа способна сгенерировать минимальный документ, доба­вим в отчет немного данных Для этого внесите следующие изменения: В нашем сценарии возникла проблема Обратили внимание, что строка System Information Report повторяется дважды? Вообще, для такого крохотного сценария это не такая большая проблема, но представьте по-настоящему длинный сценарий, в котором эта строка повторяется много раз Если в таком сценарии понадобится изменить название, придется внести изменения во множестве мест, а это масса ручной работы Можно ли изменить сценарий так, чтобы строка опре­делялась в нем только один раз? Это существенно упростило бы сопровождение сценария в будущем Да, это возможно, например, так: Создав переменную с именем title и присвоив ей значение System Information Report, мы воспользовались преимуществами подстановки параметров и поме­стили строку во множество мест

Создание переменных и констант

Но как создать переменную? Просто, — достаточно использовать ее Когда ко­мандная оболочка встречает переменную, она автоматически создает ее Этим она отличается от многих языков программирования, в которых переменные должны явно объявляться или определяться до ее использования Командная оболочка слишком либеральна в этом отношении, что в итоге приводит к некоторым про­блемам Мы сначала присвоили значение yes переменной foo и затем вывели ее значение командой echo. Далее, мы попробовали вновь вывести значение переменной, но допустили опечатку, указав имя fool, и получили пустую строку Такой резуль­тат объясняется тем, что командная оболочка благополучно создала перемен­ную fool, встретив ее, и присвоила ей пустое значение по умолчанию. Из этого примера следует, что нужно внимательно следить за правописанием! Также важ­но понять, что в действительности произошло в этом примере Из предыдуще­го знакомства с особенностями работы механизма подстановки мы знаем, что команда С другой стороны, команда [me@linuxbox ~]$ echo $fool превращается в [me@linuxbox ~]$ echo На место пустой переменной ничего не подставляется! Это может вызвать ошиб­ку в командах, требующих наличия аргументов Например: cp: после 'foo. txt' пропущен операнд, задающий целевой файл По команде "cp --help" можно получить дополнительную информацию. Мы присвоили значения двум переменным, foo и foo1 . А затем попытались вы­полнить команду cp, но допустили опечатку в имени второго аргумента. После обработки механизмом подстановки команда cp получила только один аргумент, хотя требует двух Ниже приводятся несколько правил именования переменных: Название переменная подразумевает значение, которое может изменяться, и во многих приложениях переменные именно так и используются Однако пере­менная title в нашем приложении используется как константа. Константа, так же как переменная, имеет имя и содержит значение Отличие лишь в том, что значение константы не изменяется В приложении, осуществляющем геоме­трические расчеты, можно определить константу PI со значением 3.1415, вместо того, чтобы использовать это число по всей программе Командная оболочка не различает константы и переменные; эти термины используются в основном для удобства программиста Типичное соглашение — использовать буквы верхнего регистра для обозначения констант и буквы нижнего регистра для истинных переменных Давайте изменим сценарий, приведя его в соответствие с этим со­глашением: Попутно мы дополнили название, добавив в конец значение переменной команд­ной оболочки HOSTNAME. Это — сетевое имя машины. Мы подошли к моменту, когда наше знание особенностей работы механизма под­становки начинает приносить свои плоды. Как мы видели, присваивание значе­ний переменным производится так: где переменная — это имя переменной, а значение — строка. В отличие от неко­торых других языков программирования, командная оболочка не заботится о ти­пах значений, присваиваемых переменным; она все значения интерпретирует как строки Существует возможность заставить командную оболочку ограничить круг присваиваемых значений целыми числами, задействовав команду declare с пара­метром - i, но, как и объявление переменных, доступных только для чтения, эта возможность редко используется на практике Обратите внимание на отсутствие пробелов в операторе присваивания между именем переменной, знаком «равно» и значением. А из чего может состоять значе­ние? Из всего что угодно, что можно развернуть в строку. При использовании подстановки имена переменных можно заключать в необяза­тельные фигурные скобки {}. Это пригодится в том случае, когда имя переменной становится неоднозначным в окружающем контексте В следующем примере вы­полняется попытка переименовать файл myfile в myfile1 с использованием пере­менной: [me@linuxbox ~]$ mv: после 'myfile' пропущен операнд, задающий целевой файл По команде "mv --help" можно получить дополнительную информацию. Эта попытка не увенчалась успехом, потому что командная оболочка интерпрети­ровала второй аргумент команды mv как имя новой (и пустой) переменной Ниже показано, как решается подобная проблема: Добавив фигурные скобки, мы гарантировали, что командная оболочка не будет интерпретировать последний символ 1 как часть имени переменной. Воспользуемся этой возможностью, чтобы добавить в отчет дополнительные дан­ные, а именно дату и время составления отчета, а также имя пользователя, соста­вившего отчет:

Встроенные документы

Мы рассмотрели два разных метода вывода текста, и оба используют команду echo. Однако существует еще один, третий метод, который называется встроенным до­кументом (here document), или встроенным сценарием (here script) . Встроенный документ — это дополнительная форма перенаправления ввода/вывода, которая передает текст, встроенный в сценарий, на стандартный ввод команды Действует это перенаправление так: команда << где команда — это имя команды, принимающей указанный текст через стандарт­ный ввод, а индикатор — это строка, отмечающая конец встроенного текста. Из­меним сценарий, задействовав в нем встроенный документ: Теперь вместо команды echo в сценарии используются команда cat и встроенный документ. На роль индикатора была выбрана строка _EOF_ (означает end-of-file — конец файла, распространенное соглашение), и она отмечает конец встроенного текста Обратите внимание, что строка-индикатор должна находиться в отдель­ной строке, одна, и за ней не должно следовать никаких пробелов Но какие преимущества дало использование встроенного документа здесь? Прак­тически никаких, кроме того, что кавычки внутри встроенных документов теряют свое специальное значение для командной оболочки Ниже приводится пример использования встроенного документа в командной строке: Как видите, командная оболочка не обращает никакого внимания на кавычки Она интерпретирует их как обычные символы. Благодаря этому мы свободно вставляем кавычки во встроенные документы Этим обстоятельством можно вос­пользоваться при разработке программ составления отчетов Встроенные документы можно использовать с любыми командами, принимающи­ми данные со стандартного ввода В следующем примере встроенный документ используется для передачи последовательности команд программе ftp, чтобы за­грузить файл с удаленного FTP-сервера: Если заменить оператор перенаправления << на <<-, командная оболочка будет игнорировать начальные символы табуляции во встроенном документе Благода­ря этому во встроенный документ можно добавить отступы для большей удобо­читаемости: В этой главе мы приступили к разработке проекта, при помощи которого пройдем через все этапы создания сценария Мы познакомились с переменными и констан­тами и особенностями их использования. Они чаще других программных компо­нентов применяются для подстановки Мы также увидели, как организовать вы­вод информации в сценарии, и познакомились с разными методами встраивания блоков текста С увеличением размеров и сложности программ их становится все труднее про­ектировать, программировать и сопровождать Практически к любому сложному проекту с успехом можно применить методологию деления больших и сложных задач на более мелкие и простые Представьте, что нам нужно описать типичную повседневную задачу — сходить в магазин и купить продукты — для пришельца с Марса. Весь процесс можно раз­бить на следующую последовательность шагов: Подзадачу «Выключить двигатель» можно разбить на еще более мелкие шаги, на­пример «Выключить зажигание», «Вынуть ключ зажигания» и так далее, пока все шаги посещения магазина не будут определены во всех деталях Подобный процесс идентификации высокоуровневых шагов и проработку все бо­лее мелких деталей этих шагов называют проектированием сверху вниз. Этот при­ем позволяет разбивать большие, сложные задачи на множество мелких и простых задач Проектирование сверху вниз часто используется в разработке программно­го обеспечения и хорошо подходит для программирования на языке командной оболочки В этой главе воспользуемся приемом проектирования сверху вниз для дальней­шей разработки сценария генератора отчетов

Функции командной оболочки

В настоящий момент наш сценарий генерирует документ HTML, выполняя сле­дующие шаги: На следующем этапе разработки мы добавим несколько задач между шагами 7 и 8: Если бы у нас были команды, решающие перечисленные задачи, мы бы просто добавили их в сценарий, воспользовавшись механизмом подстановки результатов команд: #!/bin/bash Создать такие команды можно двумя способами: написать три отдельных сцена­рия и поместить их в каталог, входящий в список PATH, или встроить эти сцена­рии в программу в виде функций командной оболочки. Как уже отмечалось ранее, функции — это «мини-сценарии», находящиеся внутри другого сценария, кото­рые работают как автономные программы. Функции имеют две синтаксические формы Первая выглядит так: function имя { команды return Обе формы эквивалентны и могут использоваться одна вместо другой. Ниже при­водится сценарий, демонстрирующий использование функций командной обо­лочки: Когда командная оболочка читает сценарий, она пропускает строки с 1-й по 11-ю, так как они содержат комментарии и определение функции. Выполнение начина­ется со строки 12 с командой echo. Строка 13 вызывает функцию funct, и команд­ная оболочка выполняет функцию как любую другую команду Управление пере­дается в строку 6, и выполняется вторая команда echo Следующей выполняется строка 7 Команда return в этой строке завершает выполнение функции и возвра­щает управление в строку, следующую за вызовом функции (строка 14) . После этого выполняется заключительная команда echo. Обратите внимание: чтобы вы­зовы функций интерпретировались не как имена внешних программ, а действи­тельно как вызовы функций, эти функции должны быть определены в сценарии до их вызова Добавим в наш сценарий минимальные определения функций: Имена функций подчиняются тем же правилам, что и имена переменных. Функ­ция должна содержать хотя бы одну команду. Команда return (которая является необязательной) помогает удовлетворить это требование. В сценариях, что нам доводилось писать до сих пор, все переменные (включая константы) были глобальными. Глобальные переменные существуют и доступны в любой точке программы. В некоторых случаях это безусловно полезное свой­ство осложняет использование функций Внутри функций иногда желательно ис­пользовать локальные переменные. Локальные переменные доступны только вну­три функции, в которой они определены, и прекращают свое существование по завершении выполнения функции Поддержка локальных переменных позволяет программисту использовать пере­менные с именами, которые уже определены в сценарии, глобально или в других функциях, не беспокоясь о возможных конфликтах имен Следующий пример сценария демонстрирует, как определяются и используются локальные переменные: Как видите, локальные переменные объявляются добавлением слова local перед именем переменной В результате создается переменная, локальная по отноше­нию к функции, в которой она определена Когда выполнение выйдет за пределы функции, переменная перестанет существовать. Если запустить этот сценарий, он выведет следующее Этот пример показывает, что присваивание значений локальной переменной foo внутри обеих функций не оказывает влияния на значение переменной foo, объ­явленной за пределами функций Эта особенность позволяет писать функции, сохраняя их независимость друг от друга и от сценария, в котором они определяются Это очень ценное качество, оно предотвращает взаимовлияние разных частей программы друг на друга, а кроме того, помогает писать переносимые функции, то есть функции, которые можно скопировать из одного сценария в другой

Постоянное опробование сценария

В процессе разработки программ необходимо постоянно проверять их работоспо­собность. Запуская и тестируя программы как можно чаще, мы сможем выявить ошибки на самых ранних этапах разработки. Это существенно упрощает задачу от­ладки. Например, если после внесения небольших изменений и очередного запу­ска программы обнаружится ошибка, источник проблемы почти наверняка будет находиться в последних изменениях Добавив пустые функции, которые на языке программистов называются заглушками, мы смогли проверить работоспособность программы на ранней стадии Создавая заглушку, неплохо было бы включить в нее что-то, что давало бы обратную связь, позволяющую программисту оценить ход выполнения. Если сейчас взглянуть на вывод нашего сценария, можно заме­тить несколько пустых строк, следующих за строкой с текущим временем, но мы пока не уверены в причинах их появления Для решения поставленной задачи мы использовали команду du с параметрами - sh. Однако это не полное решение задачи. Даже при том, что его можно ис­пользовать в некоторых системах (например, в Ubuntu), кое-где оно работать не будет. Причина в том, что во многих системах для домашних каталогов выбираются разрешения, не позволяющие читать их содержимое другим пользова­телям, что является вполне разумной мерой предосторожности. В этих системах функция report_home_space в том виде, в каком она написана здесь, будет рабо­тать, только если запустить сценарий с правами суперпользователя. Лучшее, что можно сделать в такой ситуации, — корректировать поведение сценария в соот­ветствии с привилегиями пользователя, запустившего его. Функции командной оболочки могут служить прекрасной заменой псевдонимам и в дей­ствительности считаются предпочтительным способом определения небольших команд для личного использования. Возможности псевдонимов весьма ограниченны в отноше­нии использования некоторых видов команд и особенностей командной оболочки, тогда как функции позволяют все, что можно выразить в виде сценария. Например, если вам понравилась функция report_disk_space, созданная нами для нашего сценария, вы можете создать похожую функцию с именем ds в своем файле. bashrc: В предыдущей главе мы столкнулись с проблемой. Как помочь сценарию адапти­ровать свое поведение в зависимости от привилегий пользователя, запустившего его? Для решения проблемы нам необходим некий способ «изменить направле­ние» выполнения сценария, опираясь на результаты проверки. Выражаясь язы­ком программистов, нам нужен способ, обеспечивающий ветвление программы. Рассмотрим простой пример логики, выраженный в псевдокоде, имитирующем язык компьютеров, но понятном человеку: В этом примере мы выполнили команду дважды. Первый раз со значением 5 в пе­ременной x, что привело к выводу строки equals 5, и второй раз со значением 0 в переменной x, что привело к выводу строки not equal 5 .где команды — это список команд. На первый взгляд такой синтаксис выглядит запутанным Но прежде чем прояснить его, посмотрим, как командная оболочка определяет, успешно или нет выполнена команда

Код завершения

Команды (включая сценарии и функции, написанные нашими собственными ру­ками) по завершении работы возвращают системе значение, которое называют кодом завершения (exit status) . Это значение — целое число в диапазоне от 0 до 255 — сообщает об успешном или неуспешном завершении команды. По согла­шениям значение 0 служит признаком успешного завершения, а любое другое — неуспешного. Командная оболочка поддерживает переменную, посредством кото­рой можно определить код завершения Например:

2В этом примере мы дважды выполнили команду ls В первый раз команда выпол­нилась благополучно Если вывести значение переменной $?, можно увидеть, что оно равно 0 . Во второй раз команда ls сообщила об ошибке, а переменная $? со­держала значение 2, указывающее, что команда столкнулась с ошибкой. Одни ко­манды используют разные коды завершения, чтобы сообщить о характере ошибки, тогда как другие, столкнувшись с любой ошибкой, просто возвращают значение 1. Страницы справочного руководства часто включают раздел с заголовком «Ехй Status» («Коды завершения»), описывающий возвращаемые коды. Однако 0 всег­да служит признаком успешного выполнения Командной оболочкой поддерживаются две чрезвычайно простые встроенные команды, которые просто завершаются с кодом 0 или 1 Команда true всегда за­вершается с признаком успеха, а команда false — всегда с признаком ошибки: Вне всяких сомнений, чаще всего с инструкцией if используется команда test Команда test может выполнять различные проверки и сравнения Она имеет две эквивалентные формы: где выражение возвращает истинное (true) или ложное (false) значение. Команда test возвращает код завершения 0, если выражение истинно, и код завершения 1, если выражение ложно Сценарий проверяет файл, имя которого присвоено константе FILE, и выводит результат. Этот сценарий имеет две интересные особенности, на которые следует обратить внимание. Во-первых, отметьте, что параметр $FILE внутри выражений заключен в кавычки Это не является обязательным требованием, но защищает от случаев, когда параметр пуст. Если механизм подстановки заменит $FILE пустым значением, это приведет к ошибке (операторы в этом случае будут интерпретиро­ваться как непустые строки, а не как операторы) . Использование кавычек гаран­тирует, что за оператором всегда будет следовать строка, даже если она пустая Во-вторых, обратите внимание на команду exit (в конце сценария) . Команда exit принимает единственный необязательный аргумент, определяющий код возврата сценария. В отсутствие аргумента exit вернет значение по умолчанию 0 . Такое использование exit позволит сценарию сообщить об ошибке, если в $FILE содер­жится имя несуществующего файла Команда exit в самом конце сценария до­бавлена исключительно для формальности Когда командная оболочка достигает конца сценария (то есть конца файла), она в любом случае завершает выполнение сценария с кодом завершения 0 Аналогично, функции могут возвращать свой код завершения, передавая цело­численный аргумент команде return. Чтобы преобразовать сценарий, приведен­ный выше, в функцию для использования в больших программах, нужно заменить команды exit инструкциями return:

При использовании с командой test операторы > и < необходимо заключать в кавычки (или экранировать символом обратного слеша) . Если этого не сделать, они будут интерпретироваться командной оболочкой как операторы перенаправления, что может привести к плачевным результатам. Обратите также внимание: в документации к ко­мандной оболочке bash утверждается, что порядок сортировки соответствует порядку алфавитной сортировки, определяемому текущими региональными настройками, но в действительности это не так. В версиях bash, вплоть до 4.0, используется порядок сор­тировки ASCII (POSIX) . Следующий сценарий демонстрирует применение выражений для проверки строк: В этом сценарии определяется константа ANSWER. Сначала сценарий проверяет, не является ли строка пустой Если строка пустая, сценарий завершается с кодом 1 Обратите внимание на оператор перенаправления в команде echo. Он перенаправ­ляет сообщение об ошибке «There is no answer» («Нет ответа») в стандартный вы­вод ошибок как «наиболее подходящий» для сообщений об ошибках. Если строка не пустая, сценарий сравнивает ее значение со строками «yes», «no» или «maybe». Проверки выполняются с использованием инструкции elif, которая является краткой формой записи для else if. Инструкция elif позволяет конструировать более сложные логические проверки

Обратите внимание на то, как сценарий определяет четность (even) или нечет­ность (odd) целого числа. Он возвращает остаток от деления числа на 2, по значе­нию которого можно судить о четности или нечетности числа

Последние версии bash

Последние версии bash реализуют составную команду, которая действует как улучшенная замена для команды test Она имеет следующий синтаксис: где выражение возвращает истинное (true) или ложное (false) значение. Команда [[ ]] очень похожа на команду test (она поддерживает те же выражения), но до­бавляет новое выражение для проверки строк: возвращающее истинное значение, если строка1 соответствует расширенному ре­гулярному выражению Это открывает широкие перспективы для решения таких задач, как проверка корректности данных Предыдущий сценарий, демонстриру­ющий применение выражений проверки целых чисел, может завершиться с ошиб­кой, если константе INT присвоить любое значение, не являющееся целым числом. Для надежности сценарию необходима возможность убедиться, что константа действительно содержит целое число Используя [[ ]] с оператором проверки строки =~, мы усовершенствуем его, как показано ниже: Применив регулярное выражение, мы смогли ограничить круг проверяемых зна­чений в константе INT только строками, начинающимися с необязательного зна­ка «минус», за которым следует одна или несколько цифр. Это выражение также устраняет вероятность появления пустых значений Еще одна дополнительная особенность [[ ]]: оператор == поддерживает сопоставление с шаблоном по аналогии с механизмом подстановки путей. Например: Она превращает [[ ]] в удобный инструмент проверки имен файлов и путей В дополнение к составной команде [[ ]] bash поддерживает также составную команду (( )), которую удобно использовать для работы с целыми числами. Она поддерживает полное множество арифметических операторов, о которых подроб­но рассказывается Команда (( )) применяется для проверки истинности арифметических выраже­ний. Арифметическое выражение считается истинным, если его результат отлича­ется от нуля Обратите внимание, что здесь мы использовали знак «меньше», а равенство про­веряется с помощью оператора Такой синтаксис выглядит более естественным при работе с целыми числами. Отметьте также, что составная команда (( )) явля­ется частью синтаксиса командной оболочки, а не обычной командой, может при­меняться только к целым числам, распознает переменные по именам и не требует выполнять подстановку Для более сложных вычислений существует возможность объединения выраже­ний. Объединяются выражения с помощью логических операторов. Мы уже встре­чались с ними в главе 17, когда изучали команду find. Всего команды test и [[ ]] поддерживают три логические операции. Это И (AND), ИЛИ (OR) и НЕ (NOT) . Для представления этих операций test и [[ ]] используют разные операторы, как показано Поскольку все выражения и операторы в команде test интерпретируются ко­мандной оболочкой как аргументы (в отличие от [[ ]] и (( ))), символы, име­ющие специальное значение для bash, такие как <, >, ( и ), необходимо заключать в кавычки или экранировать Учитывая, что команды test и [[ ]] до определенной степени равноценны, возни­кает вопрос: какой из них отдать предпочтение? Команда test является традици­онной (и частью стандарта POSIX), тогда как команда [[ ]] характерна для bash. Уметь пользоваться командой test крайне важно, потому что она применяется очень широко, но команда [[ ]] проще и удобнее в использовании Если вам доведется побеседовать с «истинными» пользователями Unix, вы быстро обнаружите, что многие из них Linux терпеть не могут. Они оценивают его как нечто нечистое и греховное. Один из принципов таких ревнителей Unix — все должно быть переносимым. То есть любой сценарий, написанный вами, должен работать без из­менений в любой Unix-подобной системе. Пользователи Unix имеют веские основания рассчитывать на это. Наблюдая послед­ствия для мира Unix, вызванные внедрением проприетарных расширений команд и командных оболочек до появления POSIX, они естественно опасаются влияния Linux на их любимую ОС.

Bash поддерживает два оператора управления

B ash поддерживает два оператора управления, которые используются для ветвле­ния. Операторы && (И) и || (ИЛИ) действуют подобно логическим операторам в составной команде [[ ]]. Они имеют следующий синтаксис: Важно понимать, как они действуют. В последовательности с оператором && пер­вая команда выполняется всегда, а вторая — только если первая завершилась успе­хом В последовательности с оператором || первая команда выполняется всегда, а вторая — только если первая завершилась неудачей. В практическом смысле это означает, что можно выполнить следующую последо­вательность команд: Она создаст каталог с именем temp и, если эта операция завершится успехом, ката­лог temp будет назначен текущим рабочим каталогом. Попытка выполнить вторую команду будет произведена, только если команда mkdir завершится успехом. Ана­логично, следующая команда проверит существование каталога temp, и только если проверка не увенчается успехом, будет выполнена команда его создания Такие конструкции очень удобно использовать для обработки ошибок в сценариях, о чем подробнее рассказывается в следующих главах. Например, в сценарии можно предусмотреть такую последо­вательность: Но переносимость имеет серьезный недостаток. Она тормозит прогресс и требует приведения всего и вся к «наименьшему общему знаменателю». Для сценариев на языке командной оболочки это означает, что они должны быть совместимы с sh, оригинальной командной оболочкой Bourne. Этот недостаток служит отговоркой, которой пользуются производители проприетарных расширений для их оправдания, только они называют их «новшествами». Но в дей­ствительности они замыкают пользователей на себя. Инструменты GNU, такие как bash, не имеют подобных ограничений. Они способствуют переносимости благодаря поддержке стандартов и всеобщей доступности. bash и другие инструменты GNU можно установить практически в любую систему, даже в Windows, совершенно бесплатно. Поэтому не бойтесь использовать все возможности, имеющиеся в командной оболочке bash. Она действительно переносима. Если сценарий требует наличия каталога temp, а он не существует, тогда сценарий завершится с кодом 1. Мы начали эту главу с вопроса, оставшегося без ответа в предыдущей главе: как сценарию sys_info_page определить, имеет ли текущий пользователь права на чтение всех домашних каталогов? После знакомства с инструкцией if эту пробле­му можно решить, добавив следующий код в функцию report_home_space: report_home_space () { Здесь проверяется вывод команды id Если вызвать команду id с параметром - u, она выведет числовой идентификатор действующего пользователя Суперполь­зователю всегда присваивается числовой идентификатор 0 . Зная это, мы скон­струировали два разных вложенных документа: один пользуется преимуществом привилегий суперпользователя, а другой ограничивается домашним каталогом текущего пользователя Теперь мы немного отдохнем от программы sys_info_page, но не волнуйтесь. Мы еще вернемся к нему. А пока затронем те темы, знание которых потребуется, когда мы возобновим разработку

Чтение ввода с клавиатуры

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

Каждый раз, когда потребуется изменить значение INT, вы должны будете изме­нить сценарий. Пользоваться сценарием было бы удобнее, если бы он предлагал пользователю ввести значение. В этой главе мы посмотрим, как придать интерак­тивность нашим программам. Встроенная команда read используется для чтения единственной строки со стан­дартного ввода. Эту команду можно использовать для чтения ввода с клавиатуры или, в случае перенаправления, строки данных из файла. Команда имеет следую­щий синтаксис: read [-параметры] [переменная...] где параметры — это один или несколько параметров из перечисленных в табл. 28 .1, а переменная — имя одной или нескольких переменных для сохранения введен­ного значения. Если имя переменной не указано, строка с данными сохраняется в переменной REPLY. В простейшем случае read сохраняет значения полей, прочитанные со стандарт­ного ввода, в указанные переменные. Ниже показано, как можно было бы изме­нить наш сценарий проверки целочисленных значений, задействовав в нем коман­ду read: Сначала мы использовали команду echo с параметром - n (подавляющим вывод символа перевода строки в конце) для вывода приглашения к вводу, а затем команду read для ввода значения в переменную int Запуск этого сценария при­водит к следующим результатам: Команда read может сохранять ввод в множестве переменных, это показано в сле­дующем сценарии: Этот сценарий вводит, присваивает переменным и выводит до пяти значений. Обратите внимание, как действует команда read, когда получает разное число Если read получит число значений меньше, чем ожидается, переменные, для ко­торых не хватило значений, останутся пустыми, а при избыточном количестве значений на входе последняя переменная получит весь остаток введенной строки Если не передать переменные команде read, весь ввод будет сохранен в перемен­ной командной оболочки REPLY:

Сценарий предлагает пользователю ввести секретный пароль и ждет 10 секунд. Если в течение этого времени ввод не был завершен, сценарий завершается с ко­дом ошибки Поскольку в команду включен параметр - s, символы пароля не вы­водятся на экран в процессе ввода Обычно командная оболочка выполняет разбиение ввода на слова перед переда­чей его команде read Как мы уже знаем, это означает, что слова во вводе, разде­ленные одним или несколькими пробелами, становятся отдельными значениями и присваиваются командой read разным переменным. Такое поведение командной оболочки регулируется переменной с именем IFS (от Internal Field Separator — внутренний разделитель полей) .

Переменная IFS

По умолчанию переменная IFS хранит символы пробела, табуляции и перевода строки, каждый из которых может служить раз­делителем полей Изменяя значение переменной IFS, можно управлять делением ввода на поля пе­ред передачей команде read. Например, файл /etc/passwd хранит строки данных, в которых поля отделяются друг от друга двоеточием. Присвоив переменной IFS значение, состоящее из единственного двоеточия, можно с помощью read прочи­тать содержимое /etc/passwd и благополучно разделить строки на поля для при­сваивания разным переменным Ниже приводится сценарий, который именно так и действует: Этот сценарий предлагает пользователю ввести имя учетной записи в системе и затем выводит разные поля, найденные в соответствующей записи в файле /etc/ passwd В сценарии есть две интересные строки Первая, отмеченная знаком О, присваивает результат команды grep переменной file_info. Регулярное выраже­ние гарантирует извлечение из файла /etc/passwd единственной строки, соответ­ствующей введенному имени пользователя

Вторая интересная строка, отмеченная знаком ©, состоит из трех частей: присва­ивания значения переменной, команды read со списком имен переменных в виде аргументов и незнакомого нам, нового оператора перенаправления Рассмотрим сначала присваивание значения переменной Командная оболочка позволяет выполнять в одной строке одно или несколько операций присваивания значений переменным непосредственно перед командой, на поведение которой эти переменные влияют Они изменяют окружение, в ко­тором выполняется команда. Действие этих операций присваивания носит вре­менный характер, окружение изменяется только на время выполнения команды В данном случае в переменной IFS сохраняется символ двоеточия. То же самое можно выразить иначе: Здесь мы сохранили прежнее значение IFS, присвоили новое значение, выполни­ли команду read и восстановили прежнее значение IFS Очевидно, что размеще­ние операции присваивания перед командой позволяет получить более компакт­ный код, действующий точно так же Даже при том, что команда read способна принимать данные со стандартного ввода, она не позволяет использовать ее следующим образом: Можно было бы ожидать, что этот прием сработает, но это не так. Внешне все будет выглядеть так, как будто команда успешно отработала, но при этом переменная reply всегда будет оставаться пустой. Почему? Объясняется это особенностью обработки конвейеров командной оболочкой. В bash (и в других командных оболочках, таких как sh) конвейеры создают подоболочки (subshells). Они являются копиями родительской оболочки и ее окружения и исполь­зуются для выполнения команд в конвейерах. В предыдущем примере команда read выполняется в подоболочке. Для подоболочек в Unix-подобных системах создаются копии родительского окруже­ния, которые они и используют в работе. Когда конвейер завершается, копия окру­жения уничтожается. Это означает, что подоболочка никогда не сможет изменить окружение родительского процесса. Как мы знаем, read присваивает значения переменным, которые становятся частью окружения. В примере выше read присвоит значение foo переменной reply в окружении подоболочки, но когда конвейер за­вершится, подоболочка и ее окружение будут уничтожены, а результат присваивания будет утрачен. Использование встроенных строк — один из способов обойти эту проблему. Еще один способ мы увидим Оператор <<< отмечает встроенную строку. Встроенная строка (here string) по­добна встроенному документу, только короче, она простирается лишь до конца текущей строки кода В данном примере строка с данными из файла /etc/passwd подается на стандартный ввод команды read. У кого-то может возникнуть вопрос, почему был выбран такой, несколько необычный, способ вместо

Проверка ввода

Использование новой для нас возможности приема ввода с клавиатуры влечет за собой дополнительную проблему: необходимость проверки введенных данных Очень часто хорошо написанная программа отличается от плохо написанной готовностью к неожиданностям. Зачастую неожиданности возникают в форме ввода ошибочных данных. Мы уже сделали кое-что, чтобы противостоять неожи­данностям в программах проверки целочисленных значений из предыдущей гла­вы, где предусмотрено отсеивание пустых значений и значений с нецифровыми символами. Такого рода программные проверки должны выполняться для любых вводимых данных, чтобы обезопасить программу от недопустимых значений Это особенно актуально для программ, используемых множеством пользователей. От­каз от защитных мер ради экономии простителен, только если программа пишется для однократного использования автором с целью решения некоей специальной задачи Но даже в этом случае, если программа выполняет потенциально опасные операции, такие как удаление файлов, на всякий случай включите в нее проверку даны Далее приводится пример программы, проверяющий входные данные разного вида Этот сценарий предлагает пользователю ввести элемент данных и затем последова­тельно анализирует его содержимое. Как видите, в сценарии использовано множе­ство идей, с которыми мы уже познакомились, включая функции [[ ]], (( )), опе­раторы управления && и if, а также разумную дозу регулярных выражений healthy. Часто для организации интерактивной работы используются меню. Программы, управляемые системой меню, выводят список возможных вариантов и предлага­ют пользователю выбрать один из них Например, представьте программу, которая выводит следующее: Этот сценарий делится на две логические части. Первая часть выводит меню и вводит выбор пользователя. Вторая часть идентифицирует выбор и выполняет соответствующие действия. Обратите внимание, как используется команда exit в этом сценарии. Она препятствует выполнению ненужного кода после заверше­ния затребованного действия. Наличие нескольких точек выхода из программы вообще считается дурным тоном (логику работы такой программы труднее по­нять), но в данном сценарии нас это устраивает. В этой главе мы сделали первый шаг к интерактивности, позволив пользователю вводить данные в наши программы с клавиатуры. Используя описанные приемы, можно написать множество полезных программ, например программы, выполня­ющие специализированные вычисления или упрощающие доступ к таинствен­ным инструментам командной строки В следующей главе мы усовершенствуем идею программ, управляемых при помощи меню, чтобы добиться большего Постарайтесь внимательно изучить программы из этой главы и достичь полного понимания их логической структуры, потому что программы, которые последуют далее, будут еще сложнее В качестве упражнения перепишите программы этой главы, используя команду test вместо составной команды [[ ]]. Подсказка: ис­пользуйте grep для сопоставления с регулярными выражениями, а затем прове­ряйте код завершения Это станет для вас хорошей практикой

Управление потоком выполнения: циклы while и until

В предыдущей главе мы написали программу, управляемую с помощью меню, для получения разного рода системной информации Программа работает, но неудоб­на в использовании Она выполняет только один выбранный вариант и заверша­ется. Хуже того, в случае ошибочного выбора программа завершается с выводом сообщения об ошибке, не давая возможности повторить попытку Пользоваться программой было бы намного удобнее, если бы она снова и снова выводила меню и предлагала сделать выбор, пока пользователь не выберет пункт, соответствую­щий выходу из программыВ этой главе мы познакомимся с приемами организации циклов, с помощью кото­рых можно реализовать многократное выполнение участков программ Команд­ная оболочка поддерживает три составные команды для организации циклов Здесь мы познакомимся с двумя из них, а с третьей Повседневная жизнь наполнена повторяющимися действиями Каждодневная поездка на работу, прогулка с собакой и нарезание моркови — все эти действия состоят из повторяющейся последовательности действий. Рассмотрим в качестве примера резку моркови Этот вид деятельности можно выразить на псевдокоде примерно так: Шаги с 4-го по 7-й образуют цикл. Действия внутри цикла повторяются, пока не будет выполнено условие «вся морковь порезана». В bash имеются средства, позволяющие выражать похожие идеи. Представьте, что нам нужно вывести пять чисел по порядку, от 1 до 5 . В сценарии на языке bash это можно реализовать, как показано ниже: Команда while имеет следующий синтаксис: while команды; do команды; done Подобно if, команда while проверяет код завершения списка команд. Пока код за­вершения равен 0, она выполняет команды внутри цикла. В сценарии, приведен­ном выше, создается переменная count, и ей присваивается начальное значение 1 Команда while проверяет код завершения команды test. Пока test возвращает код 0, команды внутри цикла продолжают выполняться В конце каждого цикла повторно выполняется команда test. После шести итераций цикла значение пере­менной count увеличится до 6, команда test вернет код завершения, отличный от 0, и цикл завершится, а программа продолжит выполнение с инструкции, следую­щей непосредственно за цикломЦикл while можно использовать для усовершенствования программы read-menu Заключив меню в цикл while, мы смогли заставить программу повторять вывод меню после каждой операции выбора. Цикл продолжает выполняться и выводить меню, пока переменная REPLY не получит значение 0, предоставляя пользователю возможность сделать другой выбор После выполнения выбранной операции вы­полняется команда sleep, она приостанавливает программу на несколько секунд и дает возможность увидеть результаты до того, как экран будет очищен и на нем вновь появится меню. Когда переменная REPLY получит значение 0, соответству­ющее варианту «Quit» (выйти), цикл завершится и выполнение продолжится со строки, следующей за done В bash имеются две встроенные команды для управления потоком выполнения внутри циклов Команда break немедленно завершает цикл, после чего выпол­нение программы продолжается с первой инструкции, следующей за циклом Команда пропускает оставшуюся часть цикла, и программа переходит к началу следующей итерации цикла. Ниже приводится версия программы while-menu, ис­пользующая обе команды — break и continue: В этой версии сценария используется бесконечный цикл (цикл, который никогда не завершится сам по себе), в котором команда while проверяет код завершения команды true. Так как true всегда возвращает код 0, цикл никогда не завершит­ся. Этот прием на удивление широко используется в сценариях. Поскольку цикл никогда не завершится сам по себе, программист должен предусмотреть его при­нудительное прерывание в нужный момент времени В этом сценарии выход из цикла осуществляется с помощью команды break, когда пользователь выберет пункт 0 . В конец других операций добавлена команда continue, чтобы увеличить эффективность работы сценария. Встретив команду continue, сценарий пере­прыгнет через остальной код в цикле, который не требуется выполнять для дан­ного выбора. Например, если пользователь выбрал пункт 1, нет никаких причин проверять выбор остальных вариантов until

Поиск и устранение ошибок

Поскольку наши сценарии становятся все сложнее и сложнее, настало время по­смотреть, что случается, когда что-то идет не так и сценарии перестают делать то, что нам нужно В этой главе мы познакомимся с некоторыми распространенными ошибками, встречающимися в сценариях, и приемами поиска и устранения неис­правностей Один из самых распространенных видов ошибок — синтаксические ошибки. Син­таксические ошибки возникают при неправильном вводе некоторого элемента с нарушением синтаксиса командной оболочки. Чаще всего эти ошибки вызывают отказ командной оболочки от выполнения сценария Для демонстрации распространенных видов ошибок в дальнейших обсуждениях мы будем использовать следующий сценарий: Команда until очень похожа на while, но завершает цикл не когда обнаружит не­нулевой код завершения, а наоборот Цикл until продолжается, пока не получит код завершения 0. В сценарии while-count цикл продолжает выполняться, пока значение переменной count меньше или равно 5 . Тот же результат можно полу­чить, переписав сценарий с командой until: С условным выражением $count - gt 5 команда until завершит цикл в нужный момент времени Выбор между циклами while и until обычно зависит от того, в каком случае условное выражение будет более читабельным Команды while и until могут принимать данные со стандартного ввода Это дает возможность обрабатывать файлы с их помощью В следующем примере мы вы­ведем содержимое файла distros. txt, созданного в одной из предыдущих глав: Чтобы перенаправить файл в цикл, мы поместили оператор перенаправления по­сле инструкции done Цикл будет вводить поля из указанного файла с помощью read. После ввода каждой строки команда read будет завершаться с кодом 0, пока не достигнет конца файла В этот момент она вернет ненулевой код завершения, и цикл завершится Цикл можно также использовать в конвейерах: Здесь вывод команды sort передается на стандартный ввод цикла, который выво­дит поток текста на экран Но не забывайте, что конвейер выполняет цикл в под - оболочке, поэтому после его завершения любые переменные, созданные в цикле, будут потеряны После знакомства с циклами и ранее представленными командами ветвления, функциями и последовательностями мы получили представление об основных способах управления потоком выполнения в программах В арсенале bash име­ется еще множество хитрых трюков, но все они основаны на этих простых Давайте изменим сценарий, удалив кавычку в конце аргумента первой коман­ды echo: Командная оболочка сгенерировала два сообщения об ошибках. Обратите вни­мание, что номера строк в сообщениях не соответствуют номеру строки, где от­сутствует кавычка Понять причину можно, мысленно последовав за программой после отсутствующей кавычки bash продолжит поиск закрывающей кавычки и найдет ее сразу за второй командой echo После этого командная оболочка bash очень удивится, обнаружив нарушение синтаксиса команды if, потому что ин­струкция fi теперь окажется внутри строки в кавычках (незакрытой) .

Найти такие ошибки в длинных сценариях порой очень сложно. Хорошую по­мощь в этом случае может оказать текстовый редактор с подсветкой синтаксиса Если в системе установлена полная версия редактора vim, подсветка синтаксиса в нем включается командой: :syntax on

Отсутствующие или неожиданные лексемы

Другая частая ошибка — отсутствие закрывающего элемента в составной команде, такой как if или while. Взгляните, что получится, если убрать точку с запятой по­сле проверки условия в команде if#!/bin/bash И снова сообщение об ошибке указывает на место, расположенное гораздо дальше фактического места ошибки Здесь складывается очень интересная ситуация Как вы помните, if принимает список команд и проверяет код завершения послед­ней команды в списке. В нашей программе мы задумали список с единственной командой [, которая является синонимом команды test Команда [ принимает все, что следует за ней, как список аргументов — в данном случае четыре аргумен­та: $number, =, 1 и ]. В отсутствие точки с запятой в список аргументов будет добав­лено слово then, что синтаксически допустимо Следующая команда echo также допустима Она интерпретируется как еще одна команда в списке команд, кото­рую if должна выполнить и проверить код завершения. Далее следует неуместное здесь слово else, потому что командная оболочка распознает его как зарезервиро­ванное слово (слово, имеющее специальное значение для командной оболочки), а не как имя команды. Это объясняет смысл сообщения об ошибке. Существуют ошибки, которые возникают лишь время от времени Иногда сцена­рий работает без ошибок, а иногда терпит неудачу из-за работы механизма под­становки Для демонстрации этой проблемы вернем точку с запятой на место и из­меним значение переменной number, присвоив ей пустое значение: получится недопустимый результат, и командная оболочка сгенерирует сообще­ние об ошибке. Оператор = является бинарным (он требует наличия двух опе­рандов, по одному с каждой стороны), но первое значение отсутствует, поэтому команда test ожидает встретить унарный оператор (такой, как - z). Далее, по­скольку test вернула ненулевой код завершения (из-за ошибки), команда if по­лучит ненулевой код завершения, примет соответствующее решение и выполнит вторую команду echo Эту проблему можно исправить, заключив в кавычки первый аргумент команды test: с правильным числом аргументов Кавычки следует использовать не только для предохранения от пустых строк, но и в том случае, если переменная содержит строку с несколькими словами, например имя файла со встроенными пробелами Логические ошибки, в отличие от синтаксических, не прерывают выполнение сце­нария. Сценарий работает, но желаемых результатов вы не дождетесь, и причина этому — проблемы с логикой Существует бесчисленное множество возможных логических ошибок, ниже перечислены наиболее типичные их виды, встречаю­щиеся в сценариях: Неправильное условное выражение. Очень легко неправильно запрограмми­ровать оператор if/then/else и получить ошибочную логику работы. Иногда логика получается полностью обратной желаемой или не охватывает весь воз­можный набор ситуаций Ошибки «смещения на единицу» . При программировании циклов со счетчи­ками можно упустить из виду, что цикл должен начинать считать с 0, а не с 1, чтобы счет закончился в нужной точке Ошибки этого вида приводят к тому, что цикл выполняет на одну итерацию больше или меньше, заканчиваясь соот­ветственно слишком поздно или слишком рано Непредвиденные ситуации. Большинство логических ошибок приводят к тому, что программа сталкивается с данными или с ситуацией, не предусмо­тренными программистом. К ним относятся непредвиденная подстановка, как, например, в случае с именами файлов, содержащими пробелы, которые преоб­разуются в несколько аргументов команды вместо одного

Защитное программирование

При программировании важно не опираться на допущения, то есть тщательно проверять коды завершения программ и команд, используемых сценарием Вот пример из реальной жизни Системный горе-администратор написал сценарий, выполняющий некую административную задачу на очень важном сервере Этот сценарий содержал следующие две строки кода: В самих строках нет никакой ошибки, при условии, что каталог, указанный в пере­менной dir_name, действительно существует. Но что случится, если это не так? Тогда команда cd потерпит неудачу, сценарий перейдет к следующей строке и уда­лит файлы в текущем рабочем каталоге. Результат, как вы понимаете, далек от ожидаемого! Несчастный администратор уничтожил массу важных файлов на сервере из-за этой логической ошибки Рассмотрим несколько способов усовершенствования описанной логики Прежде всего, можно поставить вызов команды rm в зависимость от успеха cd: В этом случае, если команда cd потерпит неудачу, команда rm не будет выполнена Так намного лучше, но еще остается вероятность отсутствия переменной dir_name или хранения в ней пустого значения, что, безусловно, приведет к удалению фай­лов в домашнем каталоге пользователя Этого можно избежать, убедившись, что dir_name действительно содержит имя существующего каталога: В подобных ситуациях, как описанных выше, лучше прервать выполнение сцена­рия с выводом сообщения об ошибке: if [[ - d $dir_name ]]; then Здесь проверяются существование каталога с указанным именем и успешное за­вершение команды cd Если какая-то из проверок завершается неудачей, в стан­дартный вывод ошибок отправляется содержательное описание и сценарий завер­шается с кодом 1, чтобы показать, что он завершился с ошибкой. Главное правило надежного программирования: если программа принимает ввод, она должна уметь обработать все, что ей передали Обычно это означает тщатель­ную отбраковку ввода с целью гарантировать, что дальнейшей обработке будут подвергнуты только допустимые данные. Пример такой проверки мы видели в предыдущей главе, когда обсуждали команду read. Там один из сценариев со­держал следующую проверку выбранного пункта меню: Когда я в студенчестве изучал промышленное проектирование, мудрый профессор учил нас, что степень проработки проекта определяется объемом времени, выделенного про­ектировщику. Если вам дано 5 минут на проектирование устройства для уничтожения воздушных целей, вы спроектируете мухобойку. А если срок — 5 месяцев, вы сможете спроектировать лазерную систему противовоздушной обороны. Тот же принцип действует и в программировании. В некоторых случаях допустимо писать сценарии на скорую руку, но только если они будут использоваться один раз и только программистом. Потребность в таких сценариях возникает довольно часто, и они должны разрабатываться быстро, без затраты лишних усилий. Подобные сценарии не требуют подробных комментариев и защитных проверок. С другой стороны, если сценарий предназначен для постоянного использования, то есть он будет использо­ваться снова и снова для решения важных задач или множеством пользователей, к его разработке следует подходить с большим тщанием. Это очень специализированная проверка Она возвращает код завершения 0, только если строка, введенная пользователем, содержит число в диапазоне от 0 до 3 . Никакой другой ввод не принимается. Иногда писать такие проверки очень утомительно, но они совершенно необходимы, если вы хотите в результате полу­чить надежно работающий сценарий

Тестирование в программировании

Тестирование — важный этап в разработке любого программного обеспече­ния, включая сценарии. В мире открытого программного обеспечения в ходу высказывание «выпускай раньше, выпускай чаще», отражающее этот факт. Про­граммное обеспечение, выпускаемое раньше и чаще, получает больше време­ни на использование и тестирование Опыт показывает, что ошибки тем легче найти и тем дешевле исправить, чем раньше в цикле разработки они будут обна­ружены Ранее мы продемонстрировали использование заглушек для проверки потока вы­полнения программы Это ценный прием проверки прогресса в работе, начиная с самых ранних стадий разработки сценария Вернемся к уже рассматривавшейся проблеме определения присутствия каталога и посмотрим, как можно было бы легко протестировать ее решение Тестировать оригинальный фрагмент довольно опасно, потому что его задача — удаление фай­лов, но его можно изменить, чтобы сделать тестирование безопасным: Так как проверка ошибочных условий уже выводит содержательные сообщения, нам не требуется добавлять ничего нового Самое важное изменение заключается в добавлении команды echo перед командой rm, которая выведет ее и список ее аргументов, но не разрешит ей выполниться Это изменение позволит безопасно выполнить код В конец фрагмента мы добавили команду exit, чтобы завершить тест и предотвратить выполнение любых других частей сценария Необходимость этого шага зависит от предназначения сценария Мы также включили несколько комментариев, которые служат «маркерами» из­менений, имеющих отношение к тестированию С их помощью легко можно найти и удалить эти изменения по завершении тестирования Чтобы извлечь пользу из тестирования, важно создавать и применять качествен­ные комплекты тестов. Для этого следует тщательно подобрать данные для вво­да или условия работы, отражающие крайние и пограничные ситуации. В нашем фрагменте кода (который очень прост) мы хотим проверить, как действует код в трех случаях: Проверив каждое из этих условий, мы получим приличный охват тестированием. Так же как в случае с проектированием, тестирование есть функция от времени Не каждую особенность сценария нужно тщательно тестировать В действитель­ности выбор фрагментов для тестирования зависит от того, что считается важным. Поскольку наш фрагмент может нести разрушительные последствия, он заслужи­вает и тщательного проектирования, и тщательного тестирования Если тестирование выявляет проблему в сценарии, следующим шагом является отладка. Под «проблемой» обычно понимается несоответствие результатов рабо­ты сценария ожиданиям программиста В этом случае нужно точно отследить, что сценарий делает и почему Поиск ошибок иногда очень напоминает детективное расследование Тщательное проектирование сценария может помочь в этом Согласно принципу защитного программирования, сценарий должен обнаруживать ненормальные условия и выводить содержательные сообщения. Иногда, однако, возникают стран­ные и неожиданные проблемы, требующие применения более сложных приемов защиты В некоторых сценариях, особенно длинных, иногда полезным оказывается исполь­зование приема изолирования области сценария, связанной с проблемой Проблема не всегда является ошибкой, но изоляция часто помогает понять суть происходя­щего. Один из приемов изоляции заключается в том, чтобы «закомментировать» фрагмент сценария. Например, попробуем изменить наш фрагмент, удаляющий со­держимое каталога, чтобы определить, имеет ли он отношение к ошибке: Поместив символы комментария в начало каждой строки внутри логического раз­дела сценария, мы предотвратили возможность выполнения этого раздела. После­дующее повторное тестирование покажет, связан ли исключенный код с ошибоч­ным поведением

Трассировка в программировании

Ошибки часто становятся причиной неожиданного направления выполнения сце­нария То есть фрагменты сценария могут никогда не выполняться или выпол­няться в неправильном порядке или в неправильные моменты. Чтобы увидеть, как в действительности протекает выполнение программы, воспользуемся при­емом трассировки.

Один из способов трассировки заключается в размещении информативных со­общений в разных точках сценария, сообщающих, где протекает выполнение На­пример, добавим в наш фрагмент следующие сообщения: сообщения посылаются в стандартный вывод ошибок, чтобы отделить их от обычного вывода. Кроме того, отсутствуют отступы перед строками с сообщения­ми, — это упростит их поиск, когда придет время убрать эти строки. Теперь, запустив сценарий, убедимся, что удаление файлов действительно было выполнено:

Кроме того, bash поддерживает встроенный метод трассировки, реализованный в виде параметра - x и команды set с параметром - x Возьмем для примера сцена­рий trouble, написанный ранее, и активируем встроенный механизм трассировки для всего сценария, добавив параметр - x в первую строку: Включенный механизм трассировки позволяет увидеть, какой вид приобрета­ют команды после применения подстановки Начальные знаки «плюс» помога­ют отличить трассировочную информацию от обычного вывода Знак «плюс» — это символ по умолчанию, используемый для вывода трассировки Он хранится в переменной командной оболочки PS4 (prompt string 4 — строка приглашения 4) . Изменим значение этой переменной, чтобы сделать трассировочный вывод более полезным Ниже мы изменили эту переменную, включив в трассировочный вывод текущий номер выполняемой строки в сценарии Обратите внимание на необхо­димость использования одиночных кавычек — это предотвращает подстановку до момента, когда строка приглашения не будет использоваться фактически: [me@linuxbox ~]$ export PS4Здесь мы использовали команду set с параметром - x, чтобы включить трассиров­ку, и с параметром +x, чтобы выключить ее. Этот прием используется для исследо­вания сразу нескольких проблемных фрагментов в сценарии Часто вместе с трассировкой полезно выводить содержимое переменных, чтобы иметь более полное представление о действиях сценария Обычно для этого ис­пользуются дополнительные инструкции echo: В этом тривиальном примере мы просто вывели значение переменной number и от­метили дополнительную строку комментарием, чтобы в будущем упростить ее по­иск и удаление Подобный прием особенно полезен при исследовании поведения циклов и арифметических операций в сценариях В этой главе мы продолжим знакомство с инструментами управления потоком выполнения. В главе 28 мы сконструировали простое меню и реализовали логи­ку обработки выбора его пунктов пользователем. Для этого использовалась серия команд if, выясняющих, какой из возможных вариантов выбран Такие конструк­ции часто можно увидеть в программах, причем так часто, что в некоторых языках программирования (включая командную оболочку) был реализован механизм управления потоком выполнения для случаев с множеством альтернативных ва­риантов Командная оболочка bash поддерживает составную команду выбора из несколь­ких вариантов, которая называется case Она имеет следующий синтаксис: Команда case берет значение слова — в данном примере значение переменной REPLY — и затем сопоставляет его с указанными шаблонами. Найдя соответствие, она выполняет команды, связанные с найденным шаблоном. После нахождения соответствия сопоставление с нижележащими шаблонами уже не производится. Шаблоны обрабатываются командой case точно так же, как пути механизмом под­становки. Шаблоны завершаются символом ).

Объединение нескольких шаблонов

Мы можем объединить несколько шаблонов, перечислив их через символ верти­кальной черты. В результате получается комбинированный условный шаблон, объединенный по «ИЛИ». Эта возможность может пригодиться, например, для обработки символов верхнего и нижнего регистров: Здесь мы изменили программу case-menu, предложив пользователю выбирать пункты меню вводом букв, а не цифр. Обратите внимание, что новые шаблоны позволяют вводить буквы обоих регистров — верхнего и нижнего Команда case является удобным дополнением к нашей коллекции приемов про­граммирования Как будет показано в следующей главе, она отлично подходит для решения некоторых видов задач Во всех предыдущих наших программах отсутствовала одна особенность — воз­можность принимать и обрабатывать параметры и аргументы командной строки В этой главе мы исследуем эту возможность и позволим нашим программам об­ращаться к содержимому командной строки. Командная оболочка поддерживает множество переменных, которые называются позиционными параметрами и содержат отдельные слова из командной строки. Эти переменные имеют имена от 0 до 9 . Продемонстрируем их: Даже в отсутствие аргументов переменная $0 всегда содержит первый элемент командной строки — путь к файлу выполняемой программы. Давайте передадим сценарию несколько аргументов: Командная оболочка поддерживает также переменную $#, хранящую число аргу­ментов командной строки: Но как быть, если программе передается большое число аргументов, как в следу­ющем примере: В системе, где выполнялся этот пример, механизм подстановки развернул сим­вол * в 82 аргумента. Как обработать такое количество? Командная оболочка предусматривает решение и для подобных случаев, правда, следует отметить, что изяществом оно не отличается. Команда shift выполняет «сдвиг» параметровк началу списка. Фактически, используя shift, можно обойтись единственной переменной-параметром (помимо $0, которая никогда не изменяется) . Каждый раз, когда выполняется команда shift, значение $2 перемещается в $1, зна­чение $3 перемещается в $2 и т. д. Значение $# при этом уменьшается на 1. В программе posit-param2 мы создали цикл, проверяющий число оставшихся ар­гументов и продолжающийся до тех пор, пока оно не уменьшится до нуля. Цикл выводит текущий аргумент, в каждой итерации увеличивает счетчик обработан­ных аргументов count и, наконец, выполняет shift, чтобы загрузить в $1 следую­щий аргумент Вот как работает эта программа:Эта программа выводит тип указанного файла (определяется с помощью коман­ды file) и его состояние (командой stat). Интересной особенностью программы является переменная PROGNAME. Ей присваивается результат выполнения команды basename $0. Команда basename удаляет начальную часть из пути к файлу, оставляя только базовое имя В данном примере basename удалит начальную часть из пара­метра $0, хранящего полный путь к данной программе. Такой результат удобно использовать для конструирования сообщений, например, о правилах использо­вания программы При подобном подходе можно переименовать сценарий, и при выводе сообщений новое имя программы будет использоваться автоматически Позиционные параметры используются для передачи аргументов не только в сце­нарии, но и в функции командной оболочки. Для демонстрации преобразуем сце­нарий file_info в функцию: Теперь, если сценарий, включающий функцию file_info, вызовет ее с именем фай­ла в аргументе, аргумент будет передан в функцию Благодаря этому мы получаем возможность написать множество полезных функ­ций для использования не только в наших сценариях, но и в файле. bashrc. Обратите внимание, что в этом примере вместо переменной PROGNAME использует­ся переменная командной оболочки FUNCNAME. Оболочка автоматически присваи­вает значение этой переменной в момент вызова функции Отметьте также, что $0 всегда содержит полный путь к первому элементу командной строки (то есть имя программы), а не имя функции, как можно было бы ожидать

Обработка позиционных параметров скопом

Иногда бывает необходимо выполнить операцию сразу со всеми позиционными параметрами. Например, может понадобиться написать обертку для некоторой программы, то есть сценарий или функцию, упрощающие запуск этой программы Обертка принимает список непонятных для нее параметров командной строки и просто передает его обернутой программе Для этой цели командная оболочка предоставляет два специальных параметра Они оба замещаются полным списком позиционных параметров, но имеют неко­торые тонкие отличия Описание этих параметров приводитсяВ данном примере оба параметра, $* и $@, возвращают результат из четырех слов: word, words, with и spaces. "$*" возвращает результат в виде одного слова, содер­жащего пробелы: word words with spaces. "$@" возвращает результат в виде двух слов, второе из которых включает пробелы: word и words with spaces Это соответствует нашим фактическим намерениям Этот пример показывает, что, несмотря на наличие четырех разных способов получения списка позици­онных параметров, в большинстве ситуаций предпочтительнее использовать прием с "$@", потому что он сохраняет целостность каждого позиционного па­раметра После долгой паузы мы продолжим работу над программой sys_info_page. Теперь мы добавим в нее поддержку нескольких параметров командной строки: Сначала мы добавили функцию usage для вывода сообщения, если программа вы­зывается с параметром --help или с неизвестным параметром

Затем следует цикл обработки параметров. Цикл продолжается, пока позицион­ный параметр $1 не получит пустое значение. В конце цикла вызывается команда shift, чтобы сдвинуть позиционные параметры и, в конечном итоге, гарантиро­вать завершение цикла Внутри цикла инструкция case проверяет текущий позиционный параметр на со­ответствие поддерживаемым вариантам. Если данный параметр поддерживается, выполняется соответствующая операция, если нет — выводится сообщение с ин­формацией о правилах пользования программой и сценарий завершается с при­знаком ошибки Обратите внимание, как обрабатывается параметр - f. Обнаружив этот параметр, программа выполняет команду shift, которая сдвинет аргумент параметра - f с именем файла в позиционный параметр $1. Если переменная interactive содержит непустое значение, начинается бесконеч­ный цикл, который предлагает ввести имя файла и затем обрабатывает ситуацию, если введенное имя соответствует существующему файлу Если указанный файл уже существует, пользователю на выбор предлагается три варианта: затереть су­ществующий файл, выбрать другое имя или завершить программу. Если поль­зователь предпочтет затереть существующий файл, выполняется команда break и цикл прерывается. Обратите внимание, что инструкция case различает только вариант перезаписи существующего файла и завершения программы. Любой дру­гой ответ пользователя будет приводить к переходу в начало цикла с повторным предложением ввести имя файл Для поддержки вывода в файл сначала необходимо имеющийся код вывода стра­ницы преобразовать в функцию. Необходимость такого решения станет понятна чуть позже: Код, обслуживающий логику параметра - f, находится в конце листинга, приве­денного выше. Он проверяет, определено ли имя файла и затем — доступность для записи файла с указанным именем. Для этого выполняется команда touch с последующей проверкой, что файл является обычным файлом Эти две провер­ки позволяют обработать ситуацию неправильно указанного пути (в этом случае touch потерпит неудачу) и убедиться, что существующий файл является обычным файлом Как видите, функция write_html_page вызывается, чтобы сгенерировать факти­ческое содержимое страницы, которое затем либо выводится в стандартный вы­вод (если переменная filename содержит пустое значение), либо перенаправляется в указанный файл. С помощью дополнительных позиционных параметров мы можем теперь писать довольно функциональные сценарии Позиционные параметры помогают созда­вать очень полезные функции командной оболочки для выполнения повседнев­ных задач, которые можно поместить в файл. bashrc. Наша программа sys_info_page выросла и усложнилась. Ниже приводится пол­ный листинг программы с выделенными последними изменениями

Управление потоком выполнения: цикл for

В этой заключительной главе, посвященной управлению потоком выполнения, мы познакомимся еще с одной конструкцией организации циклов в командной обо­лочке. Цикл for отличается от циклов while и until поддержкой средств обработки последовательностей. Это очень полезная возможность. Как следствие, цикл for пользуется большой популярностью среди создателей сценариев для bash. Цикл for реализован, что вполне естественно, в виде команды for. В современных версиях bash поддерживается две формы команды for. Оригинальный синтаксис команды for имеет следующий вид: где переменная — это имя переменной, значение которой будет увеличиваться в ходе выполнения цикла, слова — необязательный список элементов, которые последовательно будут присваиваться переменной, и команды — это команды, вы­полняемые в каждой итерации Команду for удобно использовать в командной строке. Рассмотрим, как она ра­ботает: В этом примере команда for получает список из четырех слов: A, B, C и D. Для об­хода этого списка выполняется четыре итерации цикла В начале каждой итерации переменной i присваивается очередное слово Внутри цикла находится команда echo, она выводит значение i, чтобы показать, что присваивание действительно выполняется. Так же как в случае с циклами while и until, цикл for заканчивается ключевым словом done По-настоящему мощной особенностью for является разнообразие способов фор­мирования списка слов. Например, можно использовать подстановку в фигурных скобках: Этот пример осуществляет поиск самой длинной строки в файле Когда в ко­мандной строке указано несколько имен файлов, сценарий вызывает процедуру strings (входит в состав пакета GNU binutils), чтобы получить список «слов» из каждого файла Цикл for обрабатывает каждое слово по очереди и определяет, является ли оно самым длинным из встречавшихся до сих пор. По завершении цикла сценарий выводит самое длинное слово Если необязательный компонент слова в команде for отсутствует, она по умолча­нию обрабатывает позиционные параметры. Чтобы показать использование этого способа, изменим сценарий longest-word:

Вы могли заметить, что во всех примерах цикла for выше использовалась переменная i. Почему? В действительности за этим выбором не стоят какие-то определенные причины, кроме стремления следовать традициям. В команде for можно использовать любую допустимую переменную, но чаще всего используется переменная i, а также j и k. Своими корнями эта традиция уходит в язык программирования Fortran. В Fortran необъ­явленные переменные, начинающиеся с букв I, J, K, L и M, автоматически становились целочисленными, тогда как переменные, начинающиеся с любой другой буквы, — дей­ствительными, или вещественными (способны хранить числа с дробной частью). Эта особенность вынуждала программистов использовать переменные I, э и к в качестве переменных цикла, так как использование их в качестве временных переменных (чем переменные цикла в действительности и являются) требовало меньших усилий. Из-за этого даже в среде программистов на Fortran ходила острота: «GOD is real, unless declared integer» (Бог действителен, пока явно не объявлен целым).Как видите, мы заменили внешний цикл while циклом for. Так как список слов в команде for отсутствует, она перебирает позиционные параметры Во внутрен­нем цикле вместо переменной i теперь используется переменная j Кроме того, нам больше не нужна команда shift В некоторые версии bash добавлена вторая форма синтаксиса команды for, напо­минающая одноименный оператор в языке программирования C, которая поддер­живается также многими другими языками где выражение!., выражение2 и выражениеЗ — это арифметические выражения, а команды — это команды, выполняемые в каждой итерации цикла. Здесь выражение! инициализирует переменную i значением 0, выражение2 позво­ляет продолжать итерации, пока значение i остается меньше 5, выражениеЗ увели­чивает на единицу значение i в конце каждой итерации Форма команды for в стиле языка C выглядит предпочтительнее, если требуется работать с числовыми последовательностями Несколько примеров ее примене­ния будут приведены в следующих двух главах. Познакомившись с командой for, внесем заключительное усовершенствование в наш сценарий sys_info_page. Теперь мы можем переписать ее, добавив вывод информации о домашнем катало­ге каждого пользователя и включив в вывод общее число файлов и подкаталогов в каждом из них:

Строки и числа

Любые компьютерные программы обрабатывают данные. В предыдущих главах основное внимание уделялось обработке данных на уровне файлов Однако мно­гие задачи решаются с использованием меньших единиц данных, таких как строки и числа. В этой главе мы рассмотрим некоторые возможности командной оболочки для работы со строками и числами. Командная оболочка поддерживает большое раз­нообразие способов подстановки параметров, которые выполняют строковые операции В дополнение к подстановке результатов арифметических выражений (о которой рассказывалось в главе 7) существует программа командной строки bc, выполняющая математические операции Механизм подстановки параметров уже рассматривался в главе 7, но там этот ме­ханизм не был описан детально, потому что большая часть его возможностей ис­пользуется в сценариях, а не в командной строке Мы уже знакомы с некоторыми формами подстановки параметров, например с подстановкой значений перемен­ных командной оболочки. Но в командной оболочке их намного больше. Простейшую форму подстановки параметров можно наблюдать в использовании переменных. Например, запись $a после подстановки превращается в содержи­мое переменной a Простые параметры можно заключать в фигурные скобки, на­пример: ${a} . Это не оказывает влияния на результат подстановки, но является необходимым, если сразу за именем переменной следует какой-то другой текст, который может сбивать с толку командную оболочку В следующем примере вы­полняется попытка сконструировать имя файла добавлением строки _file к содер­жимому переменной a Если выполнить эту последовательность команд, результатом будет пустое значе­ние, потому что командная оболочка попытается выполнить подстановку значе­ния переменной a_file вместо a. Эта проблема устраняется с помощью фигурных скобок: Мы видели также, что доступ к позиционным параметрам с порядковыми номе­рами выше 9 тоже осуществляется с помощью фигурных скобок. Например, про­читать 11-й позиционный параметр можно следующим образом: Некоторые формы подстановки параметров помогают решать проблемы с несу­ществующими, или пустыми, переменными Эти формы удобно использовать для обработки ситуаций отсутствия позиционных параметров и назначения им значе­ний по умолчанию Ниже приводится пример такой подстановки: Если параметр не определен (то есть отсутствует) или содержит пустое значение, механизм подстановки вернет значение указанного слова. Если параметр не пу­стой, механизм подстановки вернет значение параметра Вот еще один вариант подстановки, где вместо дефиса используется знак «равно»: Если параметр не определен или содержит пустое значение, механизм подстанов­ки вернет значение указанного слова и дополнительно присвоит его параметру Если параметр не пустой, механизм подстановки вернет значение параметра Ниже демонстрируется форма со знаком вопроса: ${параметр:? слово} Если параметр не определен или содержит пустое значение, механизм подстанов­ки завершит сценарий с ошибкой и выведет значение указанного слова в стан­дартный вывод ошибок Если параметр не пустой, механизм подстановки вернет значение параметра Командная оболочка может возвращать имена переменных Это используется в некоторых экзотических ситуациях префикса. Согласно документации bash, обе формы действуют совершенно одинаково Следующая команда

Операции со строками

Существует множество форм подстановки, которые можно использовать для ра­боты со строками. Многие из них особенно хорошо подходят для операций с пу­тями Форма вернет длину строки, содержащуюся в указанном параметре. Обычно роль пара­метра играет строка, но если передать @ или *, то механизм подстановки вернет число позиционных параметров Если указать отрицательное смещение, его отсчет начнется с конца строки вместо начала. Обратите внимание, что отрицательному значению должен предшество­вать пробел, чтобы предотвратить путаницу с формой ${параметр:-слово}. Длина, если указана, в этом случае также должна быть меньше 0 . Если в качестве параме­тра передать @, результатом подстановки будет длина позиционных параметров, начиная с указанного смещения возвращают значение параметра, удаляя из него начальную часть, определяемую указанным шаблоном В шаблоне допускается использовать групповые символы: например, те, что используются в подстановке путей Эти две формы отличаются тем, что форма # удаляет кратчайшее совпадение, тогда как форма ## удаляет са­мое длинное совпадение выполняют поиск с заменой в содержимом указанного параметра Если в пара­метре будет найдено совпадение с шаблоном, который может содержать груп­повые символы, это совпадение будет заменено содержимым указанной строки Первая форма заменит только первое совпадение с шаблоном. Форма // заменит все найденные совпадения Форма /# выполняет замену, только если совпадение с шаблоном найдено в самом начале строки, а форма /% выполняет замену, только если совпадение найдено в конце строки. Часть /строка можно опустить, и тогда совпавший фрагмент будет удален Механизм подстановки параметров — ценный инструмент Его возможности для работы со строками можно использовать вместо других широко используемых команд, таких как sed и cut. Применение механизма подстановки способствует увеличению производительности сценария за счет отсутствия необходимости выполнять внешние программы. Например, изменим программу longest-word из предыдущей главы, задействовав подстановку параметра ${#j} взамен под­становки команды $(echo $j | wc - c), которая к тому же выполняется в под - оболочке: Первоначальной версии потребовалось 3,618 секунды, чтобы просканировать текстовый файл, тогда как новой версии, использующей механизм подстановки параметров, понадобилось всего 0,06 секунды — весьма существенное улучшение. В главе 7 мы видели, как работает механизм подстановки результатов арифме­тических выражений Он используется для выполнения разных арифметических операций с целыми числами Ниже приводится его базовый синтаксис Он тесно связан с составной командой (( )), использовавшейся в главе 27 для вычисления арифметических выражений (оценки истинности) В предыдущих главах мы видели некоторые наиболее типичные выражения и опе­раторы, а здесь рассмотрим более полный их список познакомились с восьмеричными (в системе счисления с основани­ем 8) и шестнадцатеричными (в системе счисления с основанием 16) числами В арифметических выражениях командная оболочка позволяет использовать целочисленные константы в системах счисления с любым основанием. В табл. показаны формы записи чисел с указанием основания системы счисления Эти операторы присваивания обеспечивают удобный и компактный способ запи­си многих арифметических вычислений Особый интерес представляют операто­ры инкремента (++) и декремента (--), они увеличивают или уменьшают значение своего параметра на 1 Эти операторы заимствованы из языка программирова­ния C и внедрены в несколько других языков программирования, включая bash. Операторы инкремента и декремента могут находиться перед параметром или по­сле него Хотя в обоих случаях они увеличивают или уменьшают значение пара­метра на 1, тем не менее их местоположение играет важную роль. Если оператор помещается перед параметром, сначала выполняется операция инкремента (или декремента) и только потом возвращается измененное значение параметра Если оператор помещается за параметром, операция выполняется после возврата значе­ния. Такое поведение может показаться странным, но оно реализовано с умыслом. Взгляните на следующий пример: Если присвоить переменной foo значение 1 и затем увеличить ее значение с помо­щью оператора ++, следующего за именем переменной, выражение вернет прежнее значение 1 переменной foo Однако если вывести значение переменной второй раз, мы увидим увеличенное значение Если поместить оператор ++ перед параме­тром, мы получим более ожидаемый результат

Битовые операции

Командной оболочкой поддерживается класс операторов, которые манипулируют числами не совсем обычным способом Эти операторы действуют на уровне битов Они применяются для выполнения некоторых низкоуровневых операций, часто связанных с установкой или чтением битовых флагов Описание битовых опера­торов приводится Для большинства приложений на языке командной оболочки более полезным бу­дет префиксный оператор Операторы ++ и -- часто используются совместно с циклами. Внесем некоторые улучшения в сценарий, демонстрирующий применение оператора деления по мо­дулю, чтобы немного сократить его: При использовании логических операторов в арифметических выражениях действу­ют следующие правила: выражение, возвращающее 0, считается ложным, а выраже­ние, возвращающее ненулевое значение, — истинным. Составная команда (( )) ото­бражает результаты в обычные для командной оболочки коды завершения:

Самым странным из логических операторов выглядит тернарный (или трехмест­ный) оператор Этот оператор (заимствованный из языка программирования C) самостоятельно выполняет логическую проверку. Его можно использовать вместо инструкции if/then/else. Он оперирует тремя арифметическими выражениями (этот оператор не работает со строками), и если первое выражение оценивается как истинное (то есть возвращает ненулевое значение), выполняется второе выраже­ние. Иначе выполняется третье выражение. Опробуем его в командной строке. Этот пример реализует переключение значения переменной Каждый раз, когда вы­полняется оператор, значение переменной а переключается с 0 на 1 или обратно. Обратите внимание, что прямое присваивание в этом операторе считается недо­пустимой операцией Если попытаться выполнить присваивание, bash сообщит об ошибке: В этом сценарии мы реализовали цикл until, проверяющий значение перемен­ной finished. Первоначально переменной присвоено значение 0 (арифметическая ложь) . Цикл продолжается, пока эта переменная не получит ненулевое значение. Внутри цикла вычисляются квадрат и куб переменной-счетчика a В конце цик­ла выполняется проверка значения этой переменной. Если оно меньше 10 (мак­симальное число итераций), она увеличивается на 1, иначе переменной finished присваивается значение 1, что превращает ее в арифметическую истину, и цикл завершается Запустив сценарий, вы получите следующий результат: Мы уже знаем, что командная оболочка поддерживает все виды арифметических вычислений с целыми числами, но как быть, если понадобится реализовать ка­кие-нибудь вычисления из высшей математики или хотя бы просто задейство­вать вещественные числа? Ответ: никак. По крайней мере, средствами команд­ной оболочки. Для подобных вычислений придется использовать внешнюю программу Существует множество вариантов решения этой проблемы Одно из них — использовать программы на встроенном Perl или AWK, но, к сожалению, описание этих языков выходит далеко за рамки данной книги Другое решение — использовать специализированную программу-калькулятор В большинстве систем Linux имеется одна из таких программ — программа с име­нем bc Программа bc читает файл с исходным кодом на собственном языке, напоминаю­щем язык C, и выполняет его Сценарий на языке bc можно хранить в отдельном файле или передавать его на стандартный ввод программы. Язык bc поддержива­ет массу возможностей, включая переменные, циклы и функции, определяемые программистом Мы не будем рассматривать программу bc во всех подробностях, а познакомимся лишь с некоторыми ее возможностями, чтобы вы могли получить общее представление. Неплохое описание программы bc можно найти на страни­це справочного руководства (man) Начнем с простого примера Напишем сценарий на языке bc, складывающий два числа — 2 и 2: Первая строка сценария — это комментарий Для оформления комментариев в языке bc используется тот же синтаксис, что и в языке программирования C. Комментарии могут размещаться в нескольких строках, начинаясь с пары симво­лов /* и заканчиваясь парой */

В интерактивном режиме мы просто вводим выражения и сразу же получаем ре­зультат. Команда quit завершает интерактивный сеанс. Кроме того, существует возможность передать сценарий на стандартный ввод программы bc: В этом примере вычисляется размер ежемесячных платежей по кредиту на сумму $135 000, выданному под 7,75 % годовых на 180 месяцев (15 лет) . Обратите вни­мание на точность результата Она определяется значением специальной пере­менной scale в сценарии на языке bc Полное описание языка bc можно найти на справочной странице (man) для bc Несмотря на то что форма записи математических выражений немного отличается от используемой в командной оболочке (bc больше напоминает язык C), значительная часть сценария все же выглядит достаточно понятной, учитывая все, что мы узнали к настоящему моменту В этой главе мы узнали множество маленьких хитростей, которые могут при­годиться в практической работе С ростом опыта в создании сценариев умение эффективно работать со строками и числами обретает истинную ценность Наш сценарий loan-calc показал, что даже простые сценарии могут производить некоторые действительно полезные вычисления Даже при том, что сценарий loan-calc работает, он далек от совершенства. В ка­честве самостоятельного упражнения попробуйте улучшить сценарий loan-calc, добавив в него следующие возможности

Массивы в программировании

В предыдущей главе мы научились работать в командной оболочке со строками и числами. Типы данных, которые мы рассматривали до сих пор, в компьютерных кругах известны как скалярные переменные, то есть переменные, способные хра­нить единственное значение В этой главе мы познакомимся еще с одной структурой данных, которая называет­ся массивом, способной хранить множество значений. Массивы поддерживаются практически во всех языках программирования Командная оболочка также под­держивает их, хотя и в несколько ограниченном виде Но даже в этом случае они могут использоваться для решения многих задач программирования Массивы — это переменные, хранящие более одного значения Массивы органи­зованы подобно таблице Возьмем, к примеру, электронную таблицу Электрон­ная таблица действует подобно двумерному массиву. В ней есть строки и столбцы, и каждая отдельная ячейка имеет свой адрес, определяемый номером строки и но­мером столбца Массив устроен аналогично Массив состоит из ячеек, которые называют элементами, и каждый элемент содержит данные. Доступ к отдельному элементу осуществляется с использованием его адреса, который называется ин­дексом Большинство языков программирования поддерживает многомерные массивы. Электронная таблица — это пример многомерного массива с двумя измерениями: ширина и высота Многие языки поддерживают массивы с произвольным числом измерений, однако на практике чаще всего, пожалуй, используются двух - и трех­мерные массивыМассивы в bash ограничены единственным измерением. Их можно рассматривать как электронные таблицы с единственным столбцом. Но даже с этим ограничени­ем массивам можно найти массу применений Впервые поддержка массивов по­явилась в bash версии 2. Оригинальная командная оболочка Unix sh вообще не поддерживала их Переменным-массивам можно давать такие же имена, что и другим переменным bash, и они точно так же создаются автоматически при первом обращении к ним. Это пример присваивания значения элементу массива и обращения к нему. Пер­вая команда присваивает значение foo элементу массива a с индексом 1. Вторая команда выводит значение, хранящееся в элементе с индексом 1 Использование фигурных скобок во второй команде является обязательным условием, иначе командная оболочка будет пытаться выполнить подстановку пути, опираясь на имя элемента массива Массив можно также создать командой declare: Значения элементам массивов можно присваивать одним из двух способов При­сваивание одиночных значений осуществляется с использованием следующего синтаксиса: где имя — это имя массива, индекс — целое число (или арифметическое выраже­ние) больше или равное 0 Обратите внимание, что первый элемент массива име­ет индекс 0, а не 1. значение — строка или целое число, присваиваемое элементу массива Присвоить сразу множество значений можно с использованием следующего син­таксиса: последовательным элементам массива, начиная с элемента с индексом 0 Напри­мер, если понадобится присвоить элементам массива days сокращенные названия дней недели, это можно сделать так:

Доступ к элементам массива

Итак, где могут пригодиться массивы? Так же как многие задачи управления дан­ными могут решаться с применением программ электронных таблиц, массивы мо­гут применяться для решения множества задач программирования Рассмотрим простой пример сбора и представления данных. Напишем сценарий, проверяющий время последнего изменения файлов в указанном каталоге На основе полученных данных сценарий будет выводить таблицу, показывающую, сколько файлов было изменено в каждый час суток Такой сценарий можно ис­пользовать, например, для выяснения периодов наибольшей активности системы Сценарий с названием hours производит следующий результат: В этом примере мы запустили программу hours, передав ей текущий каталог для анализа. Она вывела таблицу, показывающую число файлов, изменявшихся в каждый час суток (0-23). Ниже показан код, осуществляющий вывод этой таб­лицы: Сценарий состоит из одной функции (usage) и основного тела с четырьмя раз­делами В первом разделе проверяется, является ли аргумент командной строки именем каталога Если нет, сценарий выводит сообщение с информацией о поряд­ке использования и завершается Второй раздел инициализирует массив hours. Для этого каждому элементу масси­ва присваивается значение 0 Массивы не требуют специальной инициализации перед использованием, но нашему сценарию важно, чтобы в массиве не остава­лось элементов с пустыми значениями Обратите внимание на необычный способ организации цикла. Используя подстановку в фигурных скобках ({0..23}), мы смогли без труда сгенерировать последовательность слов для команды for Следующий раздел осуществляет сбор данных, вызывая программу stat для каж­дого файла в каталоге С помощью cut из результата извлекается двузначный час Внутри цикла выполняется удаление ведущих нулей из поля с часом, потому что иначе командная оболочка попытается (и, в конечном счете, потерпит неудачу) интерпретировать значения с 00 по 09 как восьмеричные числа (см. табл. 34 .1) . Далее сценарий увеличивает на единицу значение элемента массива, соответству­ющего полученному часу дня Наконец, будет увеличен счетчик (count), храня­щий общее число файлов в каталоге Последний раздел в сценарии выводит содержимое массива. Сначала выводится пара строк заголовка, а затем начинает выполняться цикл, осуществляющий вы­вод в четыре колонки В заключение выводится общее число файлов Массивы поддерживают множество типовых операций, таких как удаление мас­сивов, определение их размеров, сортировка и др, которым можно найти много вариантов применения в сценариях Для доступа к каждому элементу массива используются индексы и @ Так же как и в случае с позиционными параметрами, индекс @ имеет большую практическую ценность Например: a dog a cat a fishМы создали массив animals и сохранили в нем три строки по два слова в каждой Затем выполнили четыре цикла, чтобы посмотреть, как выполняется разбиение содержимого массива на слова. Инструкции ${animals[*]} и ${animals[@]} дей­ствуют идентично, если они не заключены в кавычки. Индекс возвращает содер­жимое массива, разбитое на отдельные слова, тогда как индекс @ возвращает три «слова», соответствующие «истинному» содержимому массива

Определение числа элементов в массиве

Определить число элементов в массиве, так же как длину строки, можно с помо­щью механизма подстановки параметров Например: Мы создали массив a и записали строку foo в элемент с индексом 100. Далее с по­мощью механизма подстановки параметров мы определили длину массива, исполь­зуя при этом форму записи индекса @ . Затем определили длину элемента с индек­сом 100, содержащего строку foo. Обратите внимание, что даже при том, что мы присвоили строку элементу с индексом 100, bash сообщает, что в массиве имеется только один элемент. Такое поведение необычно для тех языков, в которых неис­пользуемые элементы массива (элементы с индексами 0-99) были бы инициализи­рованы пустыми значениями и учитывались бы при определении размера массива Так как bash позволяет создавать разреженные массивы путем присваивания зна­чений отдельным элементам, иногда требуется определить, какие элементы дей­ствительно существуют Это можно сделать с помощью механизма подстановки параметров, как показано ниже: где массив — это имя переменной-массива Как и в других случаях использова­ния * и @ в операциях подстановки, форма @, заключенная в кавычки, оказывается наиболее полезной, так как выполняет подстановку нераздробленных значений элементов: Знание количества элементов в массиве не поможет, если понадобится добавить значения в конец массива, потому что значения, возвращаемые индексами * и @, не сообщают наибольший занятый индекс в массиве. К счастью, командная оболочка предоставляет собственное решение. Оператор присваивания += автоматически добавляет значения в конец массива Ниже мы записали три значения в массив, а затем добавили в конец еще три. Так же как и при работе с электронными таблицами, при работе с массивами часто возникает необходимость отсортировать значения Командная оболочка не имеет прямой поддержки операции сортировки, но ее нетрудно реализовать самому: Сценарий копирует содержимое исходного массива (a) во второй массив (a_sorted), выполняя трюк с подстановкой команды. Этот простой прием можно использовать для выполнения самых разных операций с массивами, просто из­меняя состав конвейера В этом примере мы удалили третий элемент массива, с индексом 2 Не забывайте, что индексация элементов массива начинается с 0, а не с 1! Отметьте также, что элемент массива нужно заключить в кавычки, чтобы предотвратить подстановку путей оболочкойИнтересно отметить, что присваивание пустого значения массиву не уничтожает его содержимое: Если на странице справочного руководства (man) для bash выполнить поиск слова array, можно найти множество его упоминаний, где описываются при­емы работы с переменными-массивами. Большая часть этих описаний довольно туманна, но иногда они содержат весьма полезные сведения. Фактически масси­вы недостаточно широко используются в программировании на языке команд­ной оболочки, в основном потому, что традиционные командные оболочки для Unix (такие, как sh) не поддерживают их. Об этом недостатке остается только сожалеть, потому что массивы очень популярны в других языках программиро­вания и являются мощным инструментом, позволяющим решать многие задачи программирования Массивы и циклы по своей природе близки друг другу и часто используются вме­сте Например, следующая форма цикла хорошо подходит для вычисления индек­сов массива: В этой главе, завершающей наше путешествие, мы обратимся к совершенно слу­чайным темам Несмотря на то что в предыдущих главах мы рассмотрели множе­ство основных тем, немало особенностей bash остались неохваченными. Многие из них плохо освещены в документации и полезны в основном для тех, кто за­нимается интеграцией bash в дистрибутивы Linux. Но есть среди них и такие, ко­торые, хотя и используются нечасто, могут пригодиться при решении некоторых задач программирования Их-то мы и рассмотрим

Группы команд и подоболочки

Bash поддерживает возможность группировки команд Воспользоваться ею мож­но двумя способами: либо путем группировки команд, либо путем применения подоболочки Ниже приводятся примеры синтаксиса обоих подходов Группа команд заключается в фигурные скобки, а подоболочка оформляется кру­глыми скобками Вот и вся разница Однако обратите внимание, что из-за осо­бенностей реализации группировки команд в bash фигурные скобки должны от­деляться от команд пробелами и последняя команда должна завершаться точкой с запятой или символом перевода строки Итак, где могут пригодиться группы команд и подоболочки? Даже при том, что между ними имеются важные различия (которые будут раскрыты далее), и те и другие используются в основном для перенаправления. Рассмотрим фрагмент сценария, выполняющий перенаправление вывода множества команд: Выглядит достаточно просто: вывод трех команд перенаправляется в файл с име­нем output. txt. Воспользовавшись приемом группировки, то же самое можно вы­разить более кратко: Этот прием помог нам сэкономить силы и время на вводе текста сценария, но ис­тинная мощь групп команд и подоболочек проявляется в конвейерах Создавая конвейеры из команд, мы часто сталкиваемся с необходимостью объединения ре­зультатов нескольких команд в общий поток. Группы команд и подоболочки упро­щают эту задачу: Несмотря на внешнее сходство и возможность объединения потоков для последу­ющего перенаправления, между группами команд и подоболочками существуют важные отличия Все команды, входящие в группу, выполняются в текущей обо­лочке, подоболочка (как можно догадаться из названия) выполняет свои команды в дочерней копии текущей командной оболочки Это означает, что в момент за­пуска подоболочки создается копия текущей оболочки и передается новому эк­земпляру оболочки Когда подоболочка завершается, ее копия окружения унич­тожается, соответственно теряются любые изменения в окружении подоболочки (включая значения переменных) Поэтому если нет прямой необходимости в использовании подоболочки, предпо­чтительнее использовать группы команд Группы команд выполняются быстрее и требуют меньше памяти мы столкнулись с одной из проблем, характерных для подоболочек, когда выяснили, что команда read действует в конвейерах не так, как можно было бы ожидать Там мы сконструировали следующий конвейер: после выполнения которого переменная REPLY всегда оставалась пустой, потому что команда read выполняется в подоболочке и ее копия REPLY уничтожается по ее завершении Так как конвейеры команд всегда выполняются в подоболочке, любые команды, присваивающие значения переменным, будут сталкиваться с этой проблемой К счастью, командная оболочка поддерживает экзотическую форму подстановки, которая называется подстановкой процессов и может использоваться для преодо­ления указанных трудностей Подстановка процессов оформляется двумя способами: для процессов, отправля­ющих результаты в стандартный вывод: Подстановка процессов позволяет интерпретировать вывод подоболочки как обычный файл и осуществлять его перенаправление Так как это форма подста­новки, всегда можно узнать действительное подставляемое значение: Вывод результата подстановки командой echo показывает, что вывод подоболоч - ки передается через файл с именем /dev/fd/63 . Подстановка процессов часто используется в циклах, содержащих команду read Ниже приводится пример использования read в цикле, обрабатывающем список файлов в каталоге, созданном подоболочкой: Цикл выполняет read для каждой строки в списке с содержимым каталога. Сам список создается последней строкой в сценарии Здесь вывод подоболочки пе­ренаправляется на стандартный ввод цикла с помощью подстановки процесса Команда tail включена в конвейер, чтобы устранить первую строку в списке, ко­торая не нужна Проектируя большие и сложные сценарии, важно предусматривать их реакцию на неожиданный выход пользователя из системы или выключение компьютера во время их выполнения Если возникают подобные события, всем процессам посылается сигнал Программы, представляющие эти процессы, могут выпол­нять некие действия, гарантирующие корректное завершение с сохранением необходимых данных Допустим, к примеру, что мы написали сценарий, соз­дающий временный файл во время выполнения При внимательном подходе к проектированию мы могли бы предусмотреть удаление этого файла по завер­шении сценария Было бы неплохо также предусмотреть удаление файла в слу­чае получения сценарием сигнала, требующего преждевременного завершения программы

Необходимость удаления временных файлов

Одним из побудительных мотивов включения обработчиков сигналов в сценарии является необходимость удаления временных файлов, которые сценарии могут создавать для хранения промежуточных результатов. Выбор имен для временных файлов — целое искусство. Традиционно программы в Unix-подобных системах соз­дают свои временные файлы в каталоге /tmp, общем для всех и предназначенном именно для таких файлов. Однако из-за того что каталог является общим, возникает проблема безопасности, особенно остро проявляющаяся в программах, действующих с привилегиями суперпользователя. Помимо очевидной необходимости установ­ки соответствующих разрешений для файлов, которые могут быть доступны всем пользователям в системе, важно также давать временным файлам непредсказуемые имена. Это поможет избежать атак вида гонка за временными файлами (temp race attack). Ниже показан один из способов создания непредсказуемого (но все еще осмысленного) имени: Эта команда сконструирует имя файла из имени программы, идентификатора про­цесса (PID) и случайного целого числа. Но имейте в виду, что переменная командной оболочки $random возвращает значения только из диапазона от 1 до 32 767, не очень большого по компьютерным меркам, поэтому единственного экземпляра переменной недостаточно, чтобы противостоять заинтересованному злоумышленнику. Лучший результат дает программа mktemp (не путайте с функцией mktemp из стан­дартной библиотеки) — она автоматически выбирает имя и создает временный файл. Программа mktemp принимает аргумент с шаблоном, на основе которого конструирует имя файла. Шаблон должен включать последовательность символов X, которые будут заменены соответствующим числом случайных букв и цифр. Чем длиннее последо­вательность из символов X, тем длиннее последовательность случайных символов. Например:

Эта команда создаст временный файл и сохранит его имя в переменной tempfile. Символы X в шаблоне будут заменены случайными буквами и цифрами, соответственно окончательное имя файла (которое в данном примере включает также значение специ­ального параметра $$, возвращающего идентификатор процесса) может выглядеть, например, так: Несмотря на то что страница справочного руководства (man) для mktemp указывает, что mktemp создает имя временного файла, она также создает сам файл. В сценариях, предназначенных для запуска рядовыми пользователями, разумнее от­казаться от использования каталога /tmp и создать каталог для временных файлов в домашнем каталоге пользователя: например, так: Иногда возникает необходимость решать одновременно несколько задач. Мы зна­ем, что все современные операционные системы, даже те, которые не являются многопользовательскими, поддерживают многозадачность Сценарии тоже мож­но конструировать так, что они будут действовать в многозадачном режиме Обычно такие сценарии запускают один или несколько дочерних сценариев, ре­шающих вспомогательные задачи, пока родительский сценарий продолжает вы­полнять основной алгоритм Однако когда таким способом запускается целая се­рия сценариев, возникает проблема координации действий родителя и потомков. Например, представьте, что родитель зависит от результатов работы потомка или, наоборот, и он должен дождаться, пока другой сценарий завершится, прежде чем завершиться самому В bash имеется встроенная команда, помогающая управлять асинхронным выпол­нением в подобных ситуациях. Команда wait приостанавливает выполнение ро­дительского сценария, пока не завершится указанный процесс (то есть дочерний сценарий) В этом примере дочерний сценарий тривиально прост Фактическая работа вы­полняется родителем. Родительский сценарий запускает дочерний сценарий и пе­реводит его в фоновый режим выполнения.

Именованные каналы

В большинстве Unix-подобных систем существует возможность создавать файлы специального типа, которые называются именованными каналами (named pipe) . Именованные каналы создают соединения между двумя процессами и могут ис­пользоваться как обычные файлы Они не пользуются большой популярностью, но знать о такой возможности и уметь пользоваться ею желательно В программировании широко известна архитектура под названием клиент/сер­вер, основанная на использовании механизмов взаимодействий процессов, таких как именованные каналы или сетевые соединения Наиболее широко архитектура клиент/сервер используется в веб-приложениях, где веб-браузеры взаимодействуют с веб-серверами Веб-браузер действует как клиент, посылая запросы серверу, в ответ на которые сервер посылает веб­страницы Именованные каналы имеют некоторое сходство с файлами, но на самом деле образуют буферы, действующие по принципу очереди: первым пришел, первым вышел (First-In, First-Out, FIFO) . Так же как в случае с обычными (неименован­ными) каналами, данные записываются с одного конца канала и извлекаются из другого С применением именованных каналов можно, например, выполнять сле­дующие команды: и такая пара команд будет действовать подобно конвейеру процесс1 | процесс2 Прежде чем использовать именованный канал, его нужно создать Это делается с помощью команды mkfifo: Здесь с помощью команды mkfifo создается именованный канал с именем pipe1. Командой ls мы исследовали созданный файл, и, как видите, первой в поле с атри­бутами стоит буква p, сообщающая, что это именованный канал (pipe) . Идентификатор дочернего процесса сохраняется в переменной pid путем присваивания ей значения параметра $!, ко­торый всегда содержит идентификатор процесса последнего задания, переведен­ного в фоновый режим Родительский сценарий продолжает работу и в конце выполняет команду wait с идентификатором процесса дочернего сценария Это вызывает приостановку родительского сценария до завершения дочернего сценария, после чего родитель­ский сценарий возобновляет работу и тут же завершается Чтобы показать, как работают именованные каналы, откроем два окна терминала (или, как вариант, выполним описанные ниже действия в двух виртуальных кон­солях) В первом терминале введите простую команду и перенаправьте ее вывод в именованный канал: После нажатия клавиши ENTER появится ощущение, что команда «зависла». Это объясняется тем, что с другого конца канала данные еще не были прочитаны. В таких ситуациях говорят, что канал заблокирован. Разблокировка произой­дет автоматически, как только мы подключим процесс с другого конца канала и прочитаем данные из него Во втором окне терминала введите следующую команду: Во втором терминале появится список содержимого каталога, созданный в пер­вом окне, как результат работы команды cat. Команда ls в первом окне терминала благополучно разблокируется и завершится Итак, мы закончили наше путешествие. Единственное, что осталось, — это прак­тика, практика и еще раз практика. Даже при том, что на своем пути мы охвати­ли широкий круг вопросов, в действительности мы лишь затронули верхушку айсберга под названием «командная строка». Существуют еще тысячи программ командной строки, которые вам предстоит открыть и изучить Начните свои ис­следования с каталога /usr/bin, и вы увидите.


Карта сайта


Информационный сайт Webavtocat.ru