О блоге

Все новые материалы размещаются на Блогосайте alv.me. Старые - в процессе переноса.

06.09.2008

Хроника блога. 3

Таки выкопал ещё неаколько старых заметок, не вполне потерявших актуальности: про оболочку Zsh, текстовые редакторы Joe и Nedit. А также про формат DjVu - эта представляет собой чисто исторический интерес.

Ну а заметки про Unix-утилиты актуальности потерять не могут, так как сами эти утилиты не стареют.

И, кажется, на этом сбор старых материалов можно считать, наконец, законченным.

Средства управления акаунтами

2001-2005 гг

Средства для управления акаунтами несколько отличаются в Linux и в BSD-системах. Причем BSD-утилиты администрирования пользователей и групп являются существенно более мощными, разнообразными, удобными и универсальными, почему и сконцентрируем на них основное внимание, делая соответствующие оговорки относительно аналогичных по назначению средств Linux.

Начнем с создания пользователей. Самое простое на сей предмет средство - команда adduser, тем более, что утилита с этим именем имеется во всех POSIX-системах, по крайней мере открытых. Для создания единичного акаунта ее можно запустить без опций:

$ adduser
В ответ она в диалоговом режиме последовательно запросит следующую информацию:
  • Username - имя пользователя, требуется задать обязательно;
  • Full name - теоретически имя и фамилия пользователя, его можно оставить пустым или ввести произвольную информацию, которая составит значение поля комментария в базе данных пользователей;
  • Uid - идентификатор пользователя; если это поле оставить пустым, то он автоматически примет ближайшее свободное значение (для первого пользователя в системе - 1001);
  • Login group - основная группа пользователя, по умолчанию - собственная, совпадающая с его именем;
  • Login group is group_name. Invite exp into other groups? [] - включить ли пользователя в другие группы; если предполагается его доступ к полномочиям администратора, то здесь следует указать группу wheel;
  • Login class [default] - очень важный атрибут (см. следующую интермедию); в наших условиях лучше всего задать класс russian;
  • Shell (sh csh tcsh) [sh] - пользовательская оболочка; предлагаемый по умолчанию вариант (sh) - отнюдь не лучший, о чем подробнее я скажу чуть позже;
  • Home directory [/home/username] - имя домашнего каталога пользователя;
  • Use password-based authentication? [yes] - использовать ли авторизацию по паролю;
  • Use an empty password? (yes/no) [no] - использование пустого пароля (по нажатию клавиши Enter);
  • Use a random password? (yes/no) [no] - генерация произвольного пароля;
  • Enter password - ввод пароля;
  • Enter password again - и его повторение;
  • Lock out the account after creation? [no] - закрыть ли доступ к акаунту после его создания.

После честного и откровенного ответа на все поставленные вопросы будет выведено сообщение такого рода:

Username   : exp
Password : *****
Full Name :
Uid : 1002
Class : russian
Groups : exp
Home : /home/exp
Shell : /bin/sh
Locked : no
OK? (yes/no):

Если с этим согласиться. последует сообщение об успехе процедуры и предложение создать еще один Акаунт:

adduser: INFO: Successfully added (exp) to the user database.
Add another user? (yes/no):

Однако, если планируется создание серии однотипных учетных записей, целесообразно сначала выполнить настройку команды adduser, для чего ее следует запустить таким образом:

$ adduser -C

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

  • Login group - здесь можно задать общую группу для всех пользователей, например, users (сама группа должна быть создана предварительно, как - см. ниже);
  • Enter additional groups -список дополнительных групп, к которым будут приписаны все вновь создаваемые пользователи (например, wheel operator);
  • Login class - класс пользователей по умолчанию, взамен defaults; как я уже говорил, в качестве такового в наших условиях целесообразно определить russian;
  • Shell (sh csh tcsh) - командная оболочка (login shell) по умолчанию; если вы предпочитаете пользоваться каким-либо шеллом, здесь не указанным (например, zsh), его также можно указать здесь, внеся предварительно имя и полный путь к нему в файл /etc/shells;
  • Home directory - если планируется использование домашних каталогов, отличных от умолчальных /home/username;
  • Use password-based authentication? и прочие - общие условия парольного доступа;
  • умолчальное согласие с введенными данными и (или) их правка.

Определенные таким образом общие атрибуты сохраняются в файле /etc/adduser.conf и введенные нами ответы на их запрос в дальнейшем будут предлагаться по умолчанию.

К сказанному остается добавить только, что команда adduser автоматически сама проверяет имя и идентификаторы пользователя на предмет уникальности, предлагая для последних подходящие значения (обычно - следующие по порядку за ID предыдущего зарегистрированного пользователя), создает домашний каталог пользователя и копирует туда "скелеты" конфигурационных файлов (они берутся из файлов каталога /etc/skel - по умолчанию он пуст, и созданием соответствующих "скелетов" следует озаботиться самому), после чего делает соответствующие записи в базах данных пользователей - файлах /etc/passwd, /etc/master.passwd, /etc/group и /etc/master.group.

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

$ man 8 adduser

Команда adduser (или useradd) имеется и в Linux, однако там возможности ее несколько меньше. В частности, она не создает автоматически домашний каталог пользователя - это следует сделать вручную командой

$ mkdir /home/username

Да еще и позаботиться о установке его принадлежности пользователю и группе:

$ chown -R username /home/username && \
chgroup -R users /home/username

Впрочем, это можно сделать и в один прием:

$ chown -R username:users /home/username

Использование adduser - наиболее автоматизированный, но и наименее гибкий способ управления учетными записями. Основное его назначение - создание либо единичного акаунта, либо многочисленных учетных записей со сходными атрибутами и профильными файлами пользователей, "скелеты" которых заблаговременно создаются в каталоге /etc/skel.

Другая крайность - внесение всех данных пользователя в файл /etc/master.passwd вручную (в текстовом редакторе) и дальнейший запуск команды pwd_mkdb, которая в форме

$ pwd_mkdb -p /etc/master.passwd

внесет соответствующие изменения в остальные три файла, имеющие отношение к акаунтами. При этом поле пароля следует оставить пустым, поскольку любой набор символов в нем развертыванию в реальный пароль не поддается. И в дальнейшем не забыть определить пароль специальной командой passwd. Кроме того, в реально многопользовательской (то есть не настольной) системе следует быть уверенным, что никто другой в этот же момент времени не занимается изменением учетных записей - иначе трудно предсказать, чьи модификации возымеют силу. А заниматься этим может не только другой администратор системы, но и пользователь, изменяющий свой пароль или командную оболочку.

Далее, существует программа vipw, совмещающая оба процесса: вызов текстового редактора для внесения вручную изменений в файл /etc/master.passwd, а по выходе из него - модификацию баз пользовательских записей командой pwd_mkdb. В качестве редактора используется значение переменной EDITOR в профильном файле администратора. Если такового нет - вызывается классический редактор всех Unix-систем - vi (откуда и название программы).

Наконец, самое мощное средство для управления учетными записями пользователей вообще - команда pw. Это - специфичная для BSD-систем, универсальная утилита администрирования учетных записей пользователей и групп - их создания, изменения и удаления юзеров и групп. Для создания нового пользователя ее проще всего запустить в автоматическом режиме - в форме

$ pw useradd username

после чего она сама подберет подходящие (то есть ближайшие по порядку свободные) идентификаторы пользователя и группы, поля общих сведений, домашнего каталога и командной оболочки заполнит некими значениями по умолчанию (User ID, /home/username, /bin/sh, соответственно), прочие же оставит пустыми. Сам домашний каталог она тоже не создаст - для этого она потребует формы

$ pw useradd username -m

во исполнение которой заодно будут скопированы и "скелетные" файлы.

Кроме этого, команда pw может создавать учетную запись пользователя с заранее предопределенными атрибутами, описанными в файле /etc/pw.conf. Правда, для этого такой файл необходимо предварительно создать - командой

$ pw useradd -D

В первозданном виде он будет содержать все те же сведения по умолчанию. Однако его легко отредактировать в текстовом редакторе для внесения всех необходимых данных, которые отныне и будут выступать в качестве "умолчальных" для команды pw. К слову сказать (и это - очень ценное свойство данной команды) - учетные записи ранее созданных пользователей могут быть приведены в соответствие с новой их "скелетной" схемой с помощью команды

$pw useradd -D username

Ничто не мешает и задать несколько таких конфигурационных файлов (например, /etc/pw1.conf, /etc/pw2.conf и т.д.) для создания учетных записей пользователей разных категорий (например реальных или виртуальных, или пользователей с различными значениями атрибута class). Приведение конкретной учетной записи к какой-либо из принятых схем после этого можно выполнить командой

$ pw adduser username -C /etc/pw1.conf

она создает учетную запись нового пользователя с именем newuser и его домашним каталогом /home/newuser, в который копируются конфигурационные файлы командной оболочки. Файлы, подлежащие копированию, определяются содержимым каталога /etc/skel и могут быть отредактированы администратором в текстовом редакторе.

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

Для смены пароля предназначена команда passwd, общая для всех POSIX-систем. Запущенная от лица обычного пользователя без параметров и аргументов, она запросит для начала старый его пароль (если такового не было, этот шаг, естественно, пропускается), затем - новый пароль и его повторение (во избежание ошибки ввода).

Администратор системы с помощью команды

$ passwd username

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

Далее, пользователь может изменить еще два атрибута - общие сведения о себе (то есть поле комментария) и командную оболочку. Для этого он вправе использовать команду chpass, которая вызовет в текстовом редакторе (определенном переменной EDITOR профильного файла пользователя, за отсутствием таковой - в редакторе vi) следующие поддающиеся изменению сведения:

#Changing user database information for alv.
Shell: /bin/csh
Full Name: Alex Fedorchuk
Office Location:
Office Phone:
Home Phone:
Other information:

По завершении их модификации и выходе из редактора внесенные изменения будут зафиксированы в базах данных автоматически. Нетрудно догадаться, что для этого команда chpass должна иметь упоминавшийся в главе 8 атрибут "суидности", в чем можно убедиться посредством:

$ ls -l /usr/bin/chpass
-r-sr-xr-x 6 root wheel 32324 Jan 28 16:12 /usr/bin/chpass

Кроме этого, для изменения командной оболочки пользователя во всех POSIX-системах служит команда chsh, требующая опции -s, значением которой служит полный путь к исполняемому файлу нового шелла, и аргумента - имени пользователя. Впрочем, если шелл изменяет сам пользователь, в последнем нет необходимости.

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

$ chpass username
Login: alv
Password: lhBESi9NHbsrg
Uid [#]: 1000
Gid [# or name]: 1000
Change [month day year]:
Expire [month day year]:
Class: russian
Home directory: /home/alv
Shell: /bin/csh
Full Name: Alex Fedorchuk
Office Location:
Office Phone:
Home Phone:
Other information:

Можно видеть, что root-оператору предоставлено гораздо больше возможностей для изменения атрибутов акаунта. Что, прочем, не значит, что он может использовать их все. Так, изменение идентификатора пользователя эквивалентно созданию пользователя нового, а изменение имени просто поставит ему в соответствие тот же идентификатор. Ни то, ни другое не вызовет ничего, кроме путаницы. Идентификатор основной группы, вероятно, изменить можно, хотя трудно представить необходимость этого. Ну а о пароле уже говорилось достаточно.

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

Теоретически, для изменения учетных записей в разных POSIX-системах применяются также команды chfn и chsh, но в BSD эти файлы - лишь иные имена для команды chpass, и потому вызов любой из них автоматически влечет запуск последней.

Внести изменение в учетную запись можно и командой adduser с соответствующими опциями, например:

$ adduser -shell shell

для изменения командной оболочки,

$ adduser -class login_class

для изменения класса пользователя, и т.д. - список возможностей можно получить по команде

$ adduser --help

Кроме этого, для изменения учетных записей может применяться и универсальная команда pw с параметром usermod, именем пользователя и соответствующими (весьма многочисленными) опциями, с которыми можно ознакомиться посредством

$ man 8 pw

Удаление учетной записи пользователя, на первый взгляд, задача очень простая: достаточно удалить соответствующую строку из файла /etc/master.passwd и запустить команду pwd_mkdb для синхронизации изменений, точно также, как это делалось при создании акаунта. Или прибегнуть к программе vipw, которая сделает это сама. Однако и в том, и в другом случае будут удалены только сами учетные записи, а пользовательские каталоги, почтовые ящики, временные файлы, результаты всякого рода протоколирования действий (log-файлы) благополучно сохраняться, причем будут рассеяны по ряду каталогов (кроме /home, в основном, также по /tmp и /var). Кроме того, упоминания имени удаленного пользователя останутся в списках принадлежности к группам, конфигурационных файлах авторизации, систем доставки почты, почтовых и ftp-клиентов (и в иных местах). Что может создать проблемы, если в дальнейшем будет создан пользователь с тем же идентификатором и (или) именем.

Поэтому для бесследного истребления следов жизнедеятельности удаленного пользователя их следует сначала отыскать. Все принадлежащие ему файлы могут быть найдены командой find:

$ find / -user username

А добавив в конец строки опцию -delete (или перенаправив вывод по конвейеру команде rm) - и немедленно уничтожить их. А для поиска упоминаний имени в конфигурационных файлах можно прибегнуть к команде grep:

$ grep -e username /*

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

Поэтому может оказаться целесообразным прибегнуть к команде rmuser для автоматизации процесса. В форме

$ rmuser username

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

И конечно, ту же работу в состоянии сделать и палочка-выручалочка администратора - команда pw в форме

$ pw userdel username -r

Где следует обратить внимание на опцию -r - без нее pw удалит только пользовательскую запись из базы акаунтов.

Разумеется, существуют и средства для управления записями групп. Как уже говорилось, новая группа автоматически создается при заведении нового пользовательского акаунта. По умолчанию ее имя и численное значение идентификатора совпадают с именем и идентификатором пользователя. И для последнего эта группа в дальнейшем остается основной - то есть только о ней содержится информация в учетной записи пользователя.

Однако пользователь может принадлежать и к иным, дополнительным, группам: для этого его имя или идентификатор должен быть приписан к списку таких групп в файле /etc/group. Это может сделать администратор в любом текстовом редакторе (никаких дальнейших действий по синхронизации изменений не потребуется).

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

И конечно, для управления группами можно применить универсальное средство - pw. В форме

$ pw group add groupname

она создаст новую группу,

$ pw group del groupname

удалит существующую, а посредством

$ pw group mod groupname -m username1 username2

или

$pw group mod groupname -M username1 username2

дополнит или полностью перепишет списков членов группы, соответственно. Наконец, с помощью

$ pw usermod  username -G groupname

можно приписать пользователя к указанной группе в индивидуальном порядке.

05.09.2008

Файловые утилиты: архивация, компрессия, резервное копирование

2001-2005 гг

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

Для пользователя DOS/Windows, привыкшего к программам типа Zip/WinZip, архивация и компрессия неразрывны, как лошади в упряжке. Однако это - разные действия. Архивация - это сборка группы файлов или каталогов в единый файл, содержащий не только данные файлов-источников, но и информацию о них - имена файлов и каталогов, к которым они приписаны, атрибуты принадлежности, доступа и времени, что позволяет восстановить как данные, так и их структуру из архива в первозданном виде. Компрессия же предназначена исключительно для уменьшения объема, занимаемого файлами на диске (или ином носителе).

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

Традиционные средства архивации Unix-систем - команды cpio и tar. Суть первой, как можно понять их названия - копирование файлов в файл архива и из файла архива. Используется она в трех режимах.

Первый режим, copy-out, определяемый опцией -o (или --create), предусматривает считывание списка файлов (name list) со стандартного ввода и объединяет их в архив, который может быть направлен в архивный файл или на устройство для записи резервных копий. Список файлов для архивирования может представлять собой вывод какой-либо иной команды. Так, в примере

$ find ./* | cpio -o > arch.cpio

файлы текущего каталога, найденные командой find, при посредстве команды cpio будут направлены в архивный файл arch.cpio.

Второй режим (copy-in, опция -i, или --extract) осуществляет обратную процедуру: развертывание ранее созданного архива в текущем каталоге:

$ cpio -i < arch.cpio

Здесь нужно заметить, что если разворачиваемый архив включает подкаталоги, автоматически они созданы не будут, и последует сообщение об ошибке. Для создания промежуточных каталогов команда cpio должна использоваться с опцией -d (--make-directories).

В третьем режиме (copy-pass, опция -p, или --pass-through) команда cpio выполняет копирование файлов из одного дерева каталогов в другой, комбинируя режимы copy-out и copy-in, но без образования промежуточного архива. Список файлов для копирования (name list) считывается со стандартного ввода, а каталог назначения указывается в качестве аргумента:

$ cpio -p dir2 < name_list

Команда cpio имеет множество опций, позволяющих создавать, в частности, архивы в различных форматах (для межплатформенной переносимости). Однако я на них останавливаться не буду, отсылая заинтересованных к соответствующей man-странице: она не кажется мне удобной в применении. И упомянута здесь, во-первых, для полноты картины, во-вторых - универсальности ради (архивы cpio понимаются абсолютно всеми Unix'ами), в третьих - как одно из средств преобразования пакетов, используемых в различных дистрибутивах Linux, друг в друга. Например, утилита rpm2cpio преобразует широко распространенный формат пакетов rpm в еще более универсальный cpio.

Основным же средством архивирования во всех Unix-системах является команда tar. Обобщенный формат ее -

$ tar [options] archiv_name [arguments]

где archiv_name - обязательный аргумент, указывающий на имя архивного файла, с которым производятся действия, определяемые главными опциями. Формы указания опций для команды tar очень разнообразны. Исторически первой была краткая форма без предваряющего дефиса, что поддерживается и поныне. Однако в текущих версиях команды в целях единообразия утверждена краткая форма с предваряющим дефисом или дублирующая ее полная форма, предваряемая двумя дефисами. Некоторые опции (например --help - получение справки об использовании команды) предусмотрены только в полной форме.

Главные опции и указывают на то, какие действия следует выполнить над архивом в целом:

  • создание архива (опция c, -c или --create);
  • просмотр содержимого существующего архива (опция t, -t или --list);
  • распаковка архива (опция x, -x, --extract или --get).

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

  • r (или --append) - добавление новых файлов в конец архива;
  • u (или --update) - обновление архива с добавлением не только новых, но и модифицированных (с меньшим значением атрибута mtime) файлов;
  • -A (--catenate или --concatenate) - присоединение одного архива к другому;
  • --delete - удаление именованных файлов из архива;
  • --compare - сравнение архива с его источниками в файловой системе.

Прочие (очень многочисленные) опции можно отнести в разряд дополнительных - они определяют условия выполнения основных функций команды. Однако одна из таких дополнительных опций - f (-f или --file), значение которой - имя файла (в том числе файла устройства, и не обязательно на локальной машине), также является практически обязательной. Дело в том, что команда tar (от tape archiv) изначально создавалась для прямого резервного копирования на стриммерную ленту, и именно это устройство подразумевается в качестве целевого по умолчанию. Так что если это не так (а в нынешних условиях - не так почти наверняка), имя архивного файла в качестве значения опции f следует указывать явно. Причем некоторые реализации команды tar требуют, чтобы в списке опций она стояла последней.

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

$ tar cf arch_name.tar file1 ... file#

Если задать дополнительную опцию v, ход процесса будет отображаться на экране - это целесообразно, и в дальнейших примерах эта опция будет использоваться постоянно.

С помощью команды tar можно заархивировать и целый каталог, включая его подкаталоги любого уровня вложенности, причем - двояким образом. Так, если дать команду

$ tar cvf arch_name.tar *

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

$ tar cvf arch_name.tar dir

каталог dir будет упакован с полным сохранением его структуры.

С помощью команды

$ tar xvf arch_name.tar

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

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

$ tar xvf arch_name.tar filename

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

$ tar tf arch_name.tar

Если архив собирался по первой схеме (с именами файлов в качестве аргументов, вывод ее будет примерно следующим:

dir2/
dir2/file1
example
new
newfile
tee.png

При втором способе архивации мы увидим на выводе нечто вроде

dir1/
dir1/example
dir1/new
dir1/newfile
dir1/tee.png
dir1/dir2/
dir1/dir2/file1

В данном примере опция v была опущена. Включение ее приведет к тому, что список файлов будет выведен в длинном формате, подобном выводу команды ls -l:

drwxr-xr-x alv/alv 0 10 май 11:03 2002 dir2/
-rw-r--r-- alv/alv 0 10 май 11:03 2002 dir2/file1
...

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

Команд для компрессии файлов несколько, но реальный интерес ныне представляют две парные утилиты - gzip/gunzip и bz2/bunzip2. Первый член каждой пары, как легко догадаться из названия, отвечает преимущественно за компрессию, второй - за декомпрессию файлов (хотя посредством должных опций они легко меняются ролями).

Команда gzip - это традиционный компрессор Unix-систем, сменивший в сей роли более старую утилиту compress. Простейший способ ее использования -

$ gzip filename

где в качестве аргументов будет выступать имя файла. При этом (внимание!) исходный несжатый файл подменяется своей сжатой копией, которой автоматически присваивается расширение *.gz.

В качестве аргументов может выступать и произвольное количество имен файлов - каждый из них будет заменен сжатым файлом *.gz. Более того, посредством опции -r может быть выполнено рекурсивное сжатие файлов во всех вложенных подкаталогах. Подчеркну, однако, что никакой архивации команда gzip не производит, обрабатывая за раз только единичный файл. Фактически форма

$ gzip file1 file2 ... file#

просто эквивалент последовательности команд

$ gzip file1
$ gzip file2
...
$ gzip file#

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

Команда gzip имеет и другие опции, указываемые в краткой (однобуквенной) или полной нотации. В отличие от tar, знак дефиса (или, соответственно, двойного дефиса) обязателен в обоих случаях. Так, опциями -1 ... -9 можно задать степень сжатия и, соответственно, время исполнения процедуры: -1 соответствует минимальному, но быстрому сжатию, -9 - максимальному, но медленному. По умолчанию в команде gzip используется опция -6, обеспечивающая разумный компромисс между скоростью и компрессией.

Благодаря опции -d (--decompress) команда gzip может выполнить развертывание сжатого файла, заменяя его оригиналом без расширения *.gz. Хотя в принципе для этого предназначена команда gunzip:

$ gunzip file.gz

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

В последнее время широкое распространение получил компрессор bzip2, обеспечивающий большую (на 10-15%) степень сжатия, хотя и менее быстродействующий. Использование его практически идентично gzip, с деталями его можно ознакомиться с помощью страницы экранной документации man bzip2. Итоговый компрессированный файл получает имя вида *.bz2 и может быть распакован командой bunzip2 (или командой bzip2 -d). Следует только помнить, что форматы *.gz и *.bz2 не совместимы между собой. Соответственно, первый не может быть распакован программой bunzip2, и наоборот.

Поскольку программы tar и gz обеспечивают каждая свою сторону обработки файлов, возникает резонное желание использовать их совместно. Самый простой способ сделать это - воспользоваться командой tar с опцией z. Например, команда

$ tar cvzf dir.tar.gz dir/

Обратите внимание, что суффикс *.gz в этом случае нужно указывать в явном виде - автоматически оно к имени архива не присоединяется и компрессированный архив будет иметь вид dir.tar. Поскольку в Unix расширения имен файлов не играют той сакральной роли, что в MS DOS, это не помешает распаковке такого файла командой

$ tar xvzf dir.tar

Опция z сама по себе никакой компрессии не выполняет - она просто вызывает компрессор gzip для сжатия каждого из архивируемых файлов. Аналогичный смысл имеет и опция j - только ею для этой цели привлекается команда bzip2 (в некоторых системах для вызова последней из команды tar используется опция y).

При использовании команды tar с опцией z (или j) исходные файлы остаются в неприкосновенности. Следует, однако, помнить, что архив сжатых файлов не может быть обновлен командой tar с параметрами r или u.

Есть и другой способ совместной архивации и компрессии - просто последовательность команд

$ tar cf dir.tar *
$ gzip dir.tar

В результате образуется сжатый архив - внешне такой же файл dir.tar.gz. Хотя в принципе архив сжатых файлов и сжатый архивный файл - это разные вещи (можно заметить, что они даже различаются по объему, хотя и всего на несколько байт), сжатый архив также может быть благополучно развернут командой tar с опцией z. И столь же очевидно, что он не может быть ни пополнен, ни обновлен средствами архиватора tar.

Компрессированные архивы, созданные сочетанием программ tar и gzip/bzip2 - общепринятый в Unix-системах метод распространения файлов. Однако иногда для совместимости с ОС, не допускающими двух точек в имени файла (знаете такую ОС?), компрессированным tar-архивам присваивается суффикс *.tgz. Можно встретить и файлы с маской *.tbz2 (или даже *.tbz - именно такой вид имеют пакеты в 5-й ветке FreeBSD). Нетрудно догадаться, что это те же архивы *.tar.bz2.

Резервное копирование

Архивы, как правило, создаются для целей резервного копирования - то есть записи их на какой-либо внешний носитель. В качестве последних в настоящее время практически могут рассматриваться только внешние винчестеры и оптические диски (CD-R/RW и записываемые DVD разных форматов). И потому способы обращения с ними резонно рассмотреть тут же, в интермедии о файловых операциях.

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

Обычный способ переноса файловых систем - классическая утилита dd. Для использования ее в этом качестве достаточно указать файл устройства - источника и файл целевого устройства. Например, директива

$ dd if=/dev/ad0s1a of=/dev/ar0s1a

воспроизведет корневую файловую систему дискового раздела, указанного в качестве первого аргумента, на разделе второго диска. При этом нужно учитывать, что каталоги корневой файловой системы, представляющие точки монтирования самостоятельных файловых систем на отдельных разделах (такими обычно являются /usr, var, /home и так далее), затронуты не будут: для их реплицирования на другом носителе команду dd придется повторить с указанием соответствующих источников и целевых устройств.

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

В BSD-системах та же задача может быть решена с помощью команды cpdup. Правда, она требует предварительного создания разделов на целевом носителе, файловых систем на разделах и их монтирования в структуру текущей коревой файловой системы. Вот как используется cpdup при ручной установке ОС DragonFlyBSD (без помощи программы BSD Installer, описанной в соответствующей статье цикла об этой ОС):

$ cpdup / /mnt
$ cpdup /var /mnt/var
$ cpdup /etc /mnt/etc
$ cpdup /dev /mnt/dev
$ cpdup /usr /mnt/usr

Здесь каталоги /, /var и так далее - точки монтирования корня и отдельных его ветвей файловой системы установочного LiveCD, а /mnt, /mnt/var - заблаговременно созданные, отформатированные и смонтированные разделы на винчестере, на который инсталлируется DragonFlyBSD.

Команды обработки текстов

2001-2005 гг

Содержание
Вступление

Конечно, само по себе манипулирование файлами (копирование, перемещение и т.д.) также подразумевает изменение содержания некоторых файлов, но только одного-единственного типа (а именно - каталогов), однако собственно внутренняя сущность обычных файлов при этом не изменяется. Предметом же настоящей интермедии будут штатные средства POSIX-систем, позволяющие в той или иной мере учитывать контент файлов и манипулировать им. Разумеется, манипулирование контентом возможно только для регулярных файлов. При этом многие их разновидности (бинарные файлы, файлы графических форматов и word-процессоров) требуют для изменения своего содержания специальных средств - а именно, компиляторов и прикладных программ, в которых они создавались. Однако здесь о них разговора не будет - ибо целью моей было продемонстрировать мощь обычных команд для решения многих пользовательских задач. Правда, на самом деле команды модификации контента действенны преимущественно для файлов текстовых.

Однако круг объектов таких команд не столь уж узок, как может показаться. Ведь именно в виде обычных текстовых файлов в ОС POSIX-семейства хранится масса общесистемной информации, исполняемых сценариев, баз данных атрибутов самых разных объектов. Далее - собственно нарративные тексты любого содержания: ведь чисто текстовый формат для них куда роднее, чем всякого рода *.doc и *rtf. Ну и никем не возбраняется использовать такие команды в отношении текстов с разметкой - HTML ли, XML, TeX или еще чего. Так что поле приложения рассматриваемых команд - достаточно обширно.

Просмотр файлов

Однако прежде чем как-то манипулировать контентом файлов, желательно этот самый контент некоторым образом просмотреть. И тут можно вспомнить о команде cat, посредством которой мы некогда создавали файлы. Данная с именем файла в качестве аргумента, она выведет его содержимое на экран. Можно использовать и конструкцию перенаправления:

$ cat < filename

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

Недостаток команды cat как средства просмотра - невозможность перемещения по телу файла: выведя содержимое, она завершает свою работу. Конечно, "пролистывание" выведенного возможно, но - средствами системной консоли, а не самой команды.

Поэтому обычно для просмотра содержимого файлов используются специальные программы постраничного просмотра - т.н. pager'ы, очередной пример того, что передача этого термина исконно русским словом "пейджер" (а мне попадалось и такое) может создать совершенно превратное представление о сути дела.

В Unix-системах имеется две основные программы pager'а - more и less. Первая из них допускает только однонаправленный (вперед) просмотр и имеет слабые интерактивные возможности. Почему ныне и представляет лишь исторический интерес, так что о ней я говорить не буду. Тем более, что в современных свободных POSIX-системах она как таковая отсутствует: файл с именем /usr/bin/more, который можно обнаружить во FreeBSD и некоторых дистрибутивах Linux, на самом деле представляет собой жесткую или символическую ссылку на ту же самую программу, что и команда less. Хотя эта программа проявляет несколько различные свойства, в зависимости от того, какой из указанных команд она вызвана, функции ее от этого не меняются. Так что дальше я буду говорить только о команде less.

Самый простой способ вызова команды

$ less filename

после чего на экран выводится содержимое файла, указанного в качестве аргумента, по которому можно перемещаться в обоих направлениях, как построчно, так и постранично. В нижней строке экрана можно видеть символ двоеточия - приглашения для ввода команд различного назначения. В частности, нажатие клавиши h выводит справку по использованию less, а клавиши q - выход из программы просмотра (или из просмотра справочной системы, если она была перед этим вызвана). Если команда была вызвана как more (это достигается еще и специальной опцией - less -m), вместо символа двоеточия в нижней строке будет выведено имя файла с указанием процента просмотра:

command.txt 3%

что, однако, не воспрещает и здесь давать ее встроенные команды - вводом символа двоеточия (:) и закрепленной за командой литеры (или их сочетания).

Большинство встроенных команд less предназначено для навигации по телу файла. Осуществляется она несколькими способами:

  • с помощью стандартных клавиш управления курсором: PageDown или Spacebar (вперед на один экран), PageUp (назад на один экран), Down или Enter (вперед на одну строку), Up (назад на одну строку), Right (на пол-экрана вправо), Left (на пол-экрана влево);
  • с помощью предопределенных клавишных комбинаций, сходных с управляющими клавиатурными последовательностями командных оболочек и таких текстовых редакторов, как emacs и joe (хотя и не всегда с ними совпадающими): Control+V (на один экран вперед), Escape-V (на один экран назад), Control+N (на одну строку вперед), Control+P (на одну строку назад);
  • с помощью фиксированных символьных клавиш, иногда подобных таковым командного режима редактора vi: z и w (вперед и назад на один экран), e и y (вперед и назад на одну строку, можно использовать также привычные по vi клавиши j и k, соответственно), d и u (вперед и назад на пол-экрана).

Последний способ интересен тем, что допускает численные аргументы перед символьной командой: так, нажатие 3e приведет к перемещению на три строки вперед, а 2w - на два экрана назад.

Помимо "плавной", так сказать, навигации, можно перемещаться по файлу и скачками (jumping): нажатие клавиши с символом g (или последовательности Escape-<) позволяет переместиться к первой строке файла, клавиши G (регистр важен! дублирующий вариант - Escape->) - к последней его строке, клавиши p - к началу файла.

Кроме навигации, имеется и возможность двустороннего поиска - в направлении как конца, так и начала файла. Для поиска вперед требуется ввести символ прямого слэша (/) и за ним - искомое сочетание символов. Поиск в обратном направлении предваряется символом вопроса (?). В обоих случаях в шаблоне поиска можно использовать стандартные регулярные выражения *, ?, [список_символов] или [диапазон_символов]. Нажатие клавиши n (в нижнем регистре) приводит к повторному поиску в заданном ранее направлении, клавиши N (в верхнем регистре) - к поиску в направлении противоположном.

Управляющие комбинации команды less могут быть переопределены с помощью команды lesskey. Формат ее

$ lesskey -o output input

В качестве входных данных выступает простой текстовый файл (по умолчанию - ~/.lesskey, однако его следует создать самостоятельно), описывающий клавишные последовательности в следующем, например, виде:

#command
\r forw-line
\n forw-line
...
k back-line
...

Выходные данные - создаваемый из текстового двоичный файл, который собственно и используется командой less. Стандартное для него имя - ~/.less.

Команда less допускает одновременный просмотр нескольких файлов. Для этого ее следует вызвать в форме

$ less file1 file2 ... file#

после чего между открытыми файлами можно переключаться посредством :n (к следующему файлу), :p (к предыдущему файлу), :x (к первому файлу). Путем нажатия :d текущий файл исключается из списка просмотра. Символ двоеточия во всех этих случаях вводится с клавиатуры в дополнение к приглашению на ввод команд.

Команда less имеет великое множество опций - описание их на странице экранной документации занимает более дюжины страниц, поэтому задерживаться на них я не буду. Следует заметить только, что опции эти могут использоваться не только в командоной строке при запуске less, но и интерактивно - после символа дефиса в приглашении ввода. Так, указав там -m, можно включить т.н. промежуточный формат приглашения (с отображением процентов просмотренного объема файла), а с помощью -M - длинный (more-подобный) формат, при котором в приглашении дополнительно указываются имя файла, его положение в списке загруженных файлов, просматриваемые ныне строки:

command.html (file 2 of 10) lines 1-29/1364 2%

Значение команд постраничного просмотра файлов еще и в том, что именно с их помощью осуществляется доступ к экранной документации (man-страницам). Команда

$ man cmd_name

как было описано в предыдущей интермедии, на самом деле вызывает определенный по умолчанию pager для просмотра соответствующего файла /usr/share/man/man#/cmd_name.gz. Какой именно - определяется переменной PAGER в пользовательских настройках.

Кроме команд постраничного просмотра, существуют команды для просмотра фрагментарного. Это - head и tail, выводящие на экран некоторую фиксированную порцию файла, указанного в качестве их аргумента, с начала или с конца, соответственно. По умолчанию эта порция для обеих команд составляет десять строк (включая пустые). Однако ее можно переопределитьg произвольным образом, указав опции -n [число_линий] или -c [количество_байт]. Например, команда

$ head -n 3 filename

выведет три первые строки файла filename, а команда

$ tail -c 100 filename

его последние 100 байт. При определении выводимого фрагмента в строках название опции (n) может быть опущено - достаточно числа после знака дефиса.

Существуют и средства просмотра компрессированных файлов. Для файлов, сжатых программой gzip, можно использовать команды zcat и zmore, для спрессованных командой bzip2 - команду bzcat. Использование их ничем не отличается от аналогов для несжатых файлов - в сущности, именно они и вызываются для обеспечения просмотра. В случае команды zmore, как нетрудно догадаться, на самом деле используется команда less (сама по себе она аналога для компрессированных файлов не имеет).

Следующая важная группа операций над контентом файлов - сравнение файлов по содержанию и различные формы объединения файлов и их фрагментов. Начнем со сравнения. Простейшая команда для этого - cmp в форме

$ cmp file1 fil2

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

file1 file2 differ: char 27, line 4

Это означает, что различия между файлами начинаются с 27-го от начала файла символа (включая пробелы, символы конца строк и т.д.), который имеет место быть в строке 4. С помощью опций -l и -z можно заставить команду cmp вывести номера всех различающихся символов в десятичном или шестнадцатеричном формате, соответственно.

Более информативный вывод обеспечивает команда diff. Она также осуществляет построчное сравнение двух файлов, но выводит список строк, в которых обнаружены отличия. Например, для двух файлов вида

$ less file1
line 1
line 2
line 3
line 4
line 5

и

$less file2
line 1
line 2
line 3
line 3a
line 4
line 5

это будет выглядеть следующим образом:

$ diff file1 file2
3a4
> line 3a

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

  • a (от append) указывает на строку, отсутствующую в первом файле, но присутствующую во втором;
  • c (от change) фиксирует строки с одинаковым номером, но разным содержанием;
  • d (от delete) определяет строки, уникальные для первого файла.

То есть в данном примере для преобразования file1 в file2 в него после строки 3 должна быть вставлена строка 4 из второго файла, что символизирует вторая линия блока - > line 3a, где > означает строку из первого сравниваемого файла. Если же аргументы команды diff дать в обратном порядке, вывод ее будет выглядеть следующим образом:

$ diff file2 file1                             
4d3
< line 3a

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

$ less file3                                   
line 1
line 2
line 3a
line 4
line 5

то вывод команды

$ diff file1 file3                             
3c3
< line 3
---
> line 3a

будет означать необходимость замены третьей строки из file1 (символ <) на третью строку из file3 (символ >).

Такая форма вывода команды diff называется стандартной. С помощью опции -c можно задать т.н. контекстную форму вывода, при которой на экран направляется не только различающиеся строки, но и строки, их окружающие (то есть контекст, в котором они заключены):

diff -c file1 file2
*** file1 Sun May 12 11:44:44 2002
--- file2 Mon May 13 15:17:27 2002
***************
*** 1,5 ****
--- 1,6 ----
line 1
line 2
line 3
+ line 3a
line 4
line 5

Количество строк контента задается опцией -C:

diff -C 1 file1 file2                                      ttyv1
*** file1 Sun May 12 11:44:44 2002
--- file2 Mon May 13 15:17:27 2002
***************
*** 3,4 ****
--- 3,5 ----
line 3
+ line 3a
line 4

В этом примере значение опции -C (единица) предписывает вывод по одной строке контекстного окружения вокруг различающейся строки. Сами же различающиеся строки помечаются следующим образом: знаком - (минус, или дефис) - строки, подлежащие удалению из первого файла, знаком + (как в примере) - строки, которые должны быть добавлены, знаком ! - просто различающиеся строки.

Кроме контекстного формата, используется еще и вывод в унифицированном формате, что предписывается опцией -U [значение], в качестве значения указывается число строк. В нем для обозначения изменяемых строк используются только символы + и -, а сам вывод чуть короче, чем при использовании контекстного формата.

С помощью многочисленных опций команды diff сравнение файлов может быть детализовано и конкретизировано. Так, опция -b предписывает игнорировать "пустые" символы пробелов и табуляции в конце строк, а опция -w - вообще "лишние" пробелы (и те, и другие обычно имеют случайное происхождение). При указании опции -B игнорируются пустые строки, то есть не содержащие никаких иных символов, кроме перевода каретки; строки с символами табуляции или пробела как пустые не рассматриваются, для их игнорирования требуется опция -w. Благодаря опции -i при сравнении не принимается во внимание различие регистров символов, а опция -I regexp определяет регулярные вырвжения, строки с которыми также игнорируются при сравнении.

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

Вывод команды diff может быть перенаправлен в файл. Такие файлы различия именуются diff-файлами или, применительно к исходным текстам программ, патчами (patches), о которых будет сказано несколько позже. Именно с помощью таких патчей обычно распространяются изменения к программам (дополнения, исправления ошибок и т.д.).

В принципе, команда diff и придумана была именно для сравнения файлов исходников, над которыми ведут работу несколько (в пределе - неограниченное количество, как в случае с Linux) человек. Однако невозбранно и ее использование в мирных целях - то есть для сравнения просто повествовательных текстов. Единственное, что следует понимать при этом абсолютно ясно - то, что diff выполняет именно построчное сравнение. То есть: сравнение последовательностей символов, ограниченных символами конце строки с обеих сторон. И, соответственно, непрерывная абзацная строка в стиле emacs и vi - совсем не то же самое, что строка, образуемая в редакторе joe на границе экрана. Впрочем, это - вопрос, к которому еще не раз придется возвращаться.

Как уже было отмечено, команда diff осуществляет сравнение двух файлов (или - попарное сравнение файлов из двух каталогов). Однако, поскольку Бог, как известно, любит троицу, есть и команда diff3, позволяющая сранить именно три файла, указываемые в качестве ее аргументов. По действию она не сильно отличается от двоичного аналога. И потому изучение ее особенностей предлагается в качестве самостоятельного упражнения приверженцам троичной идеологии.

Существуют и средства для сравнения сжатых файлов. Это - zcmp и zdiff. Подобно командам просмотра, ими просто вызываются соотвествтующие команды cmp и diff. И потому использование их не имеет никаких особенностей.

От вопроса сравнения файлов плавно перейдем к рассмотрению способов их объединения. Для этого существует немало команд, из которых по справедливости первой должна идти команда cat, поскольку именно сие есть ее титульная функция (cat - от concatenation, сиречь объединения). Ранее уже упоминалось, что она способна добавлять информацию со стандартного ввода в конец существующего файла. Однако дело этим не ограничивается. В форме

$cat file1 file2 ... file# > file_all

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

А вот команда patch выступает в качестве диалектической пары для команды diff, именно она вносит в файл те изменения, которые документируются последней. Выглядит эта команда примерно так:

$patch file1 diff_file

в ответ на что последует нечто вроде следующего вывода:

Hmm...  Looks like a normal diff to me...
Patching file file1 using Plan A...
Hunk #1 succeeded at 4.
done

В результате исходная версия file1 будте сохранена под именем file1.orig, а сам он преобразован в соответствие с описанием diff-файла. Возможна и форма

patch < diff_file

В этом случае команда patch попытается сама определить имя файла-оригинала, и, если это ей не удастся, даст запрос на его ввод. Обращаю внимание на символ перенаправления ввода, поскольку если его опустить, имя dif-файла будет воспринято как первый аргумент команды (то есть имя файла-оригинала).

В качестве второго аргумента команды patch могут использоваться dif-файлы не только в стандартном, но и в контекстном или унифицированном формате. Это следует указать посредством опции -c или -u, соответственно.

Сочетание команд diff и patch очень широко используется при внесении изменений в исходные тексты программы. Так, если мы обратимся к коллекции портов FreeBSD, то в большинстве каталогов портированных программ можно обнаружить подкаталог files (например, /usr/ports/editors/joe-devel/files), содержимое которого как раз и есть те патчи, которые должны накладываться на исходные тексты программы для ее корректной сборки, установки и функционирования во FreeBSD. Сам же исходный текст при этом в неизменном, то есть распространяемом разработчиком, виде получается с его сайта (или любого другого сервера, в том числе и каталога distfiles ftp-сервера проекта FreeBSD.

Не менее часто, чем в слиянии, возникает и необходимость в разделении файлов на части. Цели этой служит команда split. Формат ее:

$ split [options] filename [prefix]

В результате исходный файл будет разбит на несколько отдельных файлов вида prefixaa, prefixab и так далее. Значение аргумента prefix по умолчанию - x (то есть без его указания итоговые файлы получат имена xaa, xab и т.д.).

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

Команда split может использоваться для разбиения файлового архива на фрагменты, соответствующие размеру резервных носителей. Так, в форме

$ split -b 1474560 arch_name

она обеспечит разбиение архива на части, какждая из которых может быть записана на стандартную трехдюймовую дискету. А посредством

$ split -b 650m arch_name

архив можно подготовить к записи на носители CD-R/RW. Напомню, что именно командой split создаются файлы дистрибутива FreeBSD. Легко догадаться, что обратное слияние таких фрагментированных файлов можно выполнить командой cat.

Однако этим возможности BSD-реализации команды split не исчерпываются. С помощью опции -p файл может быть разделена на фрагменты, ограниченные строками, сорержащими текст, приведенный в качестве значения шаблона. Так, командой

$ split -p Глава filename chapter-

текст произвольной длинны будет легко разбит на файлы, соответствующие отдельным главам, которым будут присвоены имена chapter-aa и далее. В качестве значения опции -p могут использоваться тэги HTML или символы разметки TeX, а также регулярные выражения.

Linux-реализация команды split таким свойством не обладает. Однако взамен этому в Linux есть команда csplit, именно для разделения файла по шаблону и предназначенная.

Поиск в файлах

В одном из предыдущих разделов говорилось о поиске файлов посредством команды find. Ныне же речь пойдет о поиске внутри файлового контента - то есть поиске текстовых фрагментов. Для этого в POSIX-системах используется семейство утилит grep - собственно grep, egrep и fgrep, несколько различющихся функционально. Впрочем, в большинстве систем все это суть разные имена (жесткие ссылки) одной и той же программы, именуемой GNU-реализацией grep, включающей ряд функций, свойственных ее расширенному аналогу, egrep. Соответственно, поиск текстовых фрагментов в файлах может быть вызван любой из этих команд, хотя в каждом случае функциональность их будет несколько различаться.

Однако начнем по порядку. Самой простой формой команды grep является следующая:

$ grep pattern files

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

$grep line ./*

будут найдены строки вида line во всех файлах текущего каталога. Шаблон для поиска не обязан быть односложным. Правда, если в нем используются последовательности символов, разделенные пробелами, последние должны тем или иным способом экранироваться, иначе в качестве шаблона будет воспринято только первое слово. Например, каждый пробел может предваряться символом обратного слэша (\), или просто все искомое выражение заключается в одинарные или двойные кавычки.

Шаблоны могут включать в себя регулярные выражения. Причем список таковых для команды grep существенно шире, чем для команд манипулирования файлами. Так, кроме маски любой последовательности символов (*), любого одиночного символа (?), списка и диапазона символов ([a...z] и [a-z]), могут встречаться:

  • . (точка) - маска любого одиночного (но, в отличие от маски ?, обязательно присутствующего) символа; то есть при задании шаблона lin. будут найдены строки, содержашие line или lins, но не lin;
  • ^ и $ - маски начала и конца строки, соответственно: по шаблону ^line, будут найдены строки, начинающиеся с соответствующего слова, по шаблону же line$ - им заканчивающиеся;
  • маски повторения предыдущего шаблона, \{n\} - ровно n раз, \{n,\} - не менее n раз, \{,m\} - не более m раз, \{n,m\} - не менее n раз и не более m раз.

Маски повторения относятся к так называемым расширенным регулярным выражениям. Для их использования команда grep должна быть дана с опцией -e или в форме egrep - последняя часто определяется в общесистемном или пользовательском профильном файле как псевдоним команды grep:

alias grep='egrep -s'
или

alias grep egrep

в оболочках семейств shell и csh, соответственно.

При этом становятся доступными и другие возможности поиска - например, нескольких текстовых фрагментов (соедниненных логическим оператором "ИЛИ") одновременно. Делается это двояко. Первый способ - просто перечисление искомых фрагментов, каждый из которых предваряется опцией -e (и при необходимости экранируется кавычками):

$ grep -e pattern1 -e pattern2 files

При втором способе оператор между искомыми шаблонами задается в явном виде:

$ egrep 'pattern1|pattern2' files

Таким способом очень легко, например, составить оглавление для длинного текста (при наличии некоторой системы рубрикации в нем, разумеется). Для этого достаточно дать команду вроде следующей:

$ egrep 'Часть|Глава|Раздел|Параграф' filename

Для текста, включающего html- или TeX-разметку, роль рубрик могут выполнять соответствующие ее элементы, например:

$ egrep ' <h1>|<h2>|<h3>|<h4>' filename

Вывод команды grep может быть перенаправлен в файл, а при необходимости и предварительно отсортирован с помощью соответствующих командных конструкций перенаправления и конвейеризации.

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

$ egrep '<h1>|<h2>|<h3>|<h4>' path/*.html > sitemap.html

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

Часть|Глава|Раздел|Параграф

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

$ egrep -f shablon filename

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

Список опций команды grep не исчерпывается указанными выше. Так, опция -i предписывает игнорировать различие регистров внутри искомого выражения, опция -h - подавляет вывод имен файлов (выводится только содержание найденных строк), тогда как опция -l, напротив, выводит только имена файлов, содержащих найденный шаблон, но не текстовый фрагмент, опция -n выводит номера найденных строк в соответствующих файлах. Весьма важной представляется опция -v, обеспечивающая инверсию поиска: при указании ее выводятся строки, не содержащие шаблона поиска.

Команда grep имеет и аналог для поиска в сжатых файлах - команду zgrep. Использование ее в целом аналогично, и останавливаться на нем я не буду.

Sed: средство потокового редактирования

Весьма часто при обработке текстов встает такая задача: заменить одно слово (или последовательность слов) на другое сразу во многих файлах. Как она решается "подоконными" средствами? Обычно - открытием всех подлежащих изменению документов в word-процессоре и применением функции поиска/замены последовательно в каждом из них.

Таким же способом можно воспользоваться и в POSIX-мире. Это просто, но уж больно скучно. Тем паче, что здесь есть очень эффективная альтернатива - средства потокового (неинтерактивного ) редактирования, примером которых является sed.

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

Так, при установке некоторых дистрибутивов Linux из числа Source Based вполне возможна ситуация, когда в какой-то момент времени в распоряжении пользователя не окажется никакого обычного текстового редактора. А необходимость внесения мелких изменений в конфигурационные файлы (например, в файл /etc/fstab) - возникнет. Так что делать - бежать срочно устанавливать свой любимый vim или emacs? И то, и другое - дело отнюдь не пяти минут. И тут самое время вспомнить про sed, который обязательно будет присутствовать в системе (поскольку он используется во многих установочных сценариях базовых пакетов).

Другой случай - во многих десятках, а то и сотнях, файлов требуется изменить одну-единственную строку, причем - одинаковым образом (например, заменить копирайт Васи Пупкина на Петю Лавочкина). Неужто для этой цели нужно вызывать мощный текстовый редактор, грузить в него немерянное количество документов, перемещаться тем или иным способом перемещаться к нужному фрагменту, вносить требуемое изменение? Отнюдь, ибо sed поможет и здесь, позволив выполнить изменение любого количества файлов в один в пакетном режиме.

Во всем блексе sed показывает себя при редактировании очень больших файлов (одно пролистывание которых требует немалого времени). А также - при редактировании сложных символьных последовательностей в нескольких файлах. Однажды, после очередной реконструкции моего сайта, передо мной встала задача тотальной модификации всех внутренних ссылок. Долго я с ужасом размышлял, как буду делать это в текстовом редакторе, и сколько ошибок при этом насажаю. Пока, раскинув мозгами, не нашел, как сделать это с помощью sed - быстро и, главное, безошибочно.

В самом общем виде sed требует двух аргументов - указания встроенной его команды и имени файла, к которому она должны быть применена. Впрочем, в качестве аргумента можно задать только простую команду, мало-мальски сложное действие (а команды поиска/замены принадлежат к числу сложных) необходимо определить через значения опции -e, заключенные в кавычки (одинарные или двойные - по ситуации). Что же касается имен файлов - то их в качестве аргументов можно указать сколько угодно, в том числе и с помощью масок типа *, *.txt и так далее. Правда, sed не обрабатывает содержимое вложенных подкаталогов, но это - дело поправимое (как - скоро увидим). Так что поиск и замена слова или их последовательности выполняются такой конструкцией:

$ sed -e 's/Вася Пупкин/Петя Лавочкин/' *

Здесь s - это команда поиска, Вася Пупкин - искомый текст, а Петя Лавочкин - текст для замены. В приведенной форме команда выполнит поиск и замену только первого вхождения искомого текста. Чтобы заменить текст по всему файлу, после последнего слэша (он обязателен в любом случае, без него sed не распознает конца заменяющего фрагмента) нужно указать флаг g (от global). Важно помнить, что если оставить заменяющее поле пустым, искомый текст будет просто удален.

По умолчанию sed выводит результаты своей работы на стандартный вывод, не внося изменений в файлы аргументы. Так где же здесь редактирование? Оно обеспечивается другой опцией - -i, указание которой внесет изменения непосредственно в обрабатываемый файл. В результате команда для замены, например, всех вхождений html на shtml во всех файлах текущего каталога будет выглядеть так:

$ sed -i -e 's/html/shtml' *

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

$ find . -name * -exec sed -i -e 's/html/shtml' * {} \

она с успехом справится с этой задачей.

Я привел лишь элементарные примеры использования sed. На самом деле возможности его много шире — за деталями, как обычно, следует обратиться к документации.

Очень коротко о bash

2005 г

Bash - наиболее распространенная среди пользователей Linux командная оболочка, выступающая в этой ОС к тому же общесистемной и умолчальной. Популярна она, насколько мне известно, и среди пользователей иных POSIX-систем, по крайней мере свободных. И потому не сказать о ней хоть пару слов здесь - нельзя.

Однако о bash существует немерянное количество источников информации. Достаточно заметить, что в любой толстой книге про Linux, когда речь заходит о командных оболочках, имеется ввиду именно bash. Немало сведений о ней есть и в Сети, в том числе в русскоязычном ее сегменте.

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


bash, что расшифровывается как "еще одна оболочка Борна", "заново рожденный шелл" и тому подобным образом. Она происходит от первого шелла первозданного Unix'а, разработанного Борном (которая именовалась просто shell или sh), но снабжена многими дополнительными возможностями. Эта оболочка оказалась тесно интегрирована с операционной системой Linux: именно bash волею судеб стал одной из первых программ, которые Линус запустил поверх своего новосозданного ядра. И потому идеи bash-скриптинга пронизали Linux до самых его оснований - достаточно сказать, что большинство сценариев инициализации в каталоге /etc представляют именно bash-скрипты.

Однако роль bash не исчерпывается скриптингом - в первую очередь это среда для выполнения пользовательских команд. Различаются команды внешние и встроенные команды оболочки. Bash поддерживает множество таких встроенных команд (полный список можно увидеть на man-странице bash-builtins).

Оболочка bash поддерживает все интерактивные возможности, столь важные для пользователя, как то: автодополнение для команд и путей к файлам, историю оных (включая средства инкрементного поиска), мощные возможности навигации и редактирования командной строки. Важно, что существует дополнительный пакет bash-completion (в Kubuntu он устанавливается по умолчанию): установка его обогащет базовую оболочку множеством опциональных средств настройки автодополнения (в том числе и для командных аргументов). Правда, чтобы эта дополненная оболочка была по настоящему удобной и функциональной, нужно приложить некоторые усилия по ее настройке, чем мы и займемся в данном параграфе.

Для настройки шелла важно понимать разницу между интерактивным и неинтерактивным шеллами. Первый - это любой экземпляр командной оболочки, запущенный пользователем непосредственно. Если этот экземпляр запускается при входе пользователя в систему, его называют login shell (то есть главная пользовательская оболочка). Очевидно, что login shell - также интерактивен, однако в сеансе работы каждого пользователя он будет единственным. Просто же интерактивных шеллов можно запустить сколько угодно - например, в каждом терминальном окне или вкладке konsole будет функционировать собственная копия интерактивного шелла.

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

Схема настройки bash предусматривает наличие пары файлов /etc/profile и /etc/bashrc (для пользовательского шелла и просто интерактивного его экземпляра), а также соответствующих им пользовательских конфигов - ~/.bash_profile и ~/.bashrc. При авторизации первым в любом случае считывается общесистемный профильный файл /etc/profile, вслед за ним - пользовательский профильный файл ~/.bash_profile, после чего происходит обращение к ~/.bashrc. Файл /etc/profile может занимать особое положение - в него часто помещают переменные окружения (например, локально-зависимые), которые должны быть общими для всех пользователей данной системы. Пользовательские же настройки определяются в файлах ~/.bash_profile и ~/.bashrc. Обычно в ~/.bash_profile определяются переменные окружения, которые должны действовать для всех дочерних процессов, а в ~/.bashrc - параметры, всегда требуемые в в интерактивном режиме (например, псавдонимы).

Редактирование командной строки в bash обеспечивается отдельным пакетом - библиотекой функций readline. Она имеет собственные конфигурационные файлы, общесистемный /etc/inputrc и пользовательский ~/.inputrc.

Впрочем, в Kubuntu, ориентированном на графичесёкий режим и, следовательно, использование эмулятора терминала с интерактивным шеллом, не являющимся, тем не менее, шеллом пользовательским (login shell), ~/.bash_profile играет сугубо служебную роль, и содержимое его сводится к отработке файла ~/.bashrc:

# include .bashrc if it exists
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi

# set PATH so it includes user's private bin if it exists
if [ -d ~/bin ] ; then
PATH=~/bin:"${PATH}"
fi

в котором и выполняются все пользовательские настройки.

Большинство настроек bash по умолчанию разумны, и потому наличные файлы вполне могут быть взяты за основу. Однако путем некоторых несложных действий их можно дополнить, увеличив удобство интерактивного использования командной оболочки.

Так, по умолчанию в bash автодополнение клавишей табулятора не работает, например, в аргументах многих команд, таких, как sudo или man. Решается эта задача очень просто: достаточно файл ~/.bashrc внести следующие строки:

# enable bash completion in interactive shells
if [ -f /etc/bash_completion ]; then
. /etc/bash_completion
fi

После этого автодополнение будет работать буквально везде, где только можно себе представить, например: bash - автодополнение названия хоста или dpkg --sea - получится dpkg --search.

Если в файл /etc/inpurc (или в ~/inpurc) добавить такие строки:
"\e[A": history-search-backward
"\e[B": history-search-forward

то набор части команды, например, cd /, и последующий перебор стрелками и истории команд повлечет извеление из буфера истории только тех из них, которые начинаются на cd /.

Очень полезно в файле ~/.bashrc определить псевдонимы (aliases) для некоторых часто используемых команд. Псевдоним - это просто некоторое условное имя, подменяющее определенную команду с теми ее опциями, которые мы используем чаще всего. Причем, что характерно, псевдоним команды может совпадать с ее именем. То есть, например, - набирая просто ls, мы получаем список файлов не в умолчальном формате, а в том, в каком угодно нам.

Устанавливаются псевдонимы одноименной командой alias, в качестве аргументов которой выступают имя псевдонима и его значение, соединенные оператором присваивания (именуемым в просторечии знаком равенства). А именно, если мы хотим ныне, и присно, и во веки веков видеть вывод команды ls в цвете, нам достаточно вписать в конфиг bash такой псевдоним:

$ alias ls='ls --color=auto'

Вспомним команды типа cp и mv, которыми мы можем скопировать или переместить какие-то файлы из каталога в каталог. А что произойдет, если чисто случайно в целевом каталоге уже имеются файлы, одноименные копируемым/перемещаемым? Произойдет штука, могущая иметь весьма неприятные последствия: файлы в целевом каталоге будут заменены новыми, теми, что копируются туда или перемещаются. То есть исходное содержание этих файлов будет утрачено - и утрачено безвозвратно.

Разумеется, иногда так и нужно: например, при резервном копировании старые версии файлов и должны быть заменены более свежими их вариантами. Однако такое приемлемо далеко не всегда. И потому в большинстве команд, связанных с необратимыми изменениями файловой системы, предусматривается специальная опция - -i (или --interactive). Если задать эту опцию с командой cp или mv, то при совпадении имен исходного и целевого файлов будет запрошено подтверждение на выполнение соответствующего действия. И, дабы не держать в голове необходимость опции -i, для команд cp и mv (а также для команды rm, эта операция также практически необратима) целесообразно определить одноименные им псевдонимы такого вида:

alias cp='cp -i'
alias mv='mv -i'
alias rm='rm -i'

Но что, если заведомо известно, что сотни, а то и тысячи файлов целевого каталога должны быть именно переписаны новыми своими версиями? В этом случае можно прибегнуть к опции -f (или --force). Которая, отменяя действие опции -i, предписывает принудительно переписать все файлы целевого каталога их обновленными тезками. И никто не мешает нам на этот случай создать еще один псевдоним для команды cp, например:

alias cpf='cp -f'

Второй способ временн избавиться от подтверждения на копирование или удаление файлов - просто отменить псевдоним. Что делается командой - unalias. То есть, дав директиву

$ unalias cp

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

$ alias

без опций и аргументов выведет полный их список.

Сироты комамндира Нортона

2001 г

-Кто твоя мать, рядовой Петров?
-Коммунистическая партия Советского Союза!
-Кто твой отец?
-Вы, товарищ командир!
-Какова твоя заветная мечта?
-Сиротой бы остаться...
Из старого советского анекдота

Всенародная наша любовь к файловому менеджеру коммандера Нортона и многочисленным его потомкам для любых платформ известна и в комментариях не нуждается (о причинах ее - в заметке про konqueror). Не миновала эта любовь и открытые Unix-подобные системы. Где роль главного наследника славного коммандера прочно закрепилась за Midnight Commander'ом (выступающим под псевдонимом mc), весьма точно воспроизводящим внешний облик своего родителя, но далеко превзошедшим его функционально.

Должен сознаться, что на заре моего приобщения к Linux'у без mc я не обходился: именно он, наряду с KDE, помог сломать психологический барьер перед командной строкой, выросший за годы тлетворного влияния Windows.

Однако время шло, я постепенно проникался величием традиционных Unix-средств для управления файлами, далеко превосходящими по скорости и эффективности любые Commander'ы (не говоря уж об Explorer'ах). И к волшебному сочетанию символов mc обращался все реже и реже - хотя по привычке устанавливал его всегда, чтобы было. Ну и для визуализации результатов своих действий он часто оказывался не лишним.

Первое разочарование в mc постигло меня на стадии приобщения к FreeBSD. Если его версия для Linux могла носить имя сына коммандера Нортона с гордостью, то FreeBSD-версию иначе чем командирской сироткой язык назвать не поворачивался (речь идет о 2001 годе - ныне былые недостатки Free'шной версии изжиты, подобно пьянству при Михал Сергеиче). Начать с того, что по непонятным причинам вызывался он там не волшебной аббревиатурой mc, а весьма неуклюжим и непривычным буквосочетанием midc. Что, конечно, решалось просто введением дополнительного псевдонима в профильном файле, например, для csh

alias mc midc

однако все равно раздражало. Но это еще полбеды. Далее обнаружилось отсутствие встроенного редактора: если соответствующая опция в mc (пардон, в midc) была включена, по нажатию на F4 вызывался не кто иной, как vi.

Конечно, и это поддавалось лечению - или переопределением переменной EDITOR, или использованием внешнего редактора. Благо последний мог имитироваться редактором le, идеологически и по интерфейсу весьма схожим с mcedit (и обладающим к тому же рядом полезных качеств).

Но с было трудно примириться любому старому командирскому комбатанту - так это с непотребным поведением клавишных комбинаций по умолчанию. Так, комбинация Control+Enter отнюдь не помещала файл под курсором в командную строку, а Control+O вместо отключения панелей вызывала просто немедленный (и - без предупреждения) выход из midc. И Midnight Commander под FreeBSD был мной окончательно заброшен.

Во избежание недоразумений повторяю - все сказанное относится именно к midc под FreeBSD. В Linux-своей версии mc - это мощный, полнофункциональный файловый менеджер, ничуть не уступающий FAR'у для Windows. Да и под FreeBSD, по сведениям Андрея Лаврентьева (http://unix1.jinr.ru/~lavr/), умолчальные его недостатки могут быть ликвидированы путем пересборки с соответствующими настройками, однако об этом я узнал позднее.

И тут наступил психологический момент вспомнить о другом коммандерском отпрыске для FreeBSD - Demos Commander'е (или, сокращенно, deco). Получившего свое имя, разумеется, не от древнегреческих демократов, а от пионера российского провайдерства (автор - Serge Vakulenko). И конечно, ничего специфически FreeBSD'шного не содержащего: просто в дистрибутивах Linux он обычно не встречается, а во FreeBSD (и - OpenBSD также) входит стандартно, как в виде пакета, так и порта.

Надо заметить, что с первого взгляда deco производит еще более сиротское впечатление: съежившиеся на пол-экрана панели удручающе-черного цвета (на некоторых терминалах приобретающие почему-то окраску в династических цветах дома Романовых), крайне ограниченные возможности интерактивной настройки, возможности файловых манипуляций - на уровне 1-х версий Norton Commander (в частности, невозможно скопировать, переместить или удалить каталог целиком, с содержащимися в нем файлами).

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

Однако если набраться некоторого терпения, начинаешь понимать всю сермяжную правду, заложенную в deco. Во-первых, поведение клавишных комбинаций в нем привычно (пользователю mc) и предсказуемо. Во-вторых, возможности настройки его не столь уж бедны, как кажется на первый взгляд. Есть возможность создания пользовательского меню (вызываемого по клавише F2) - неотъемлемого атрибута "командирских" файловых менеджеров. К любым типам файлов, определяемых по маскам, можно привязать не только команды, но и сколь угодно сложные их конструкции. А самое главное - в deco обнаружился замечательный режим командной строки.

Включение этого режима (через меню или комбинацией клавиш Control+P) приводит к тому, что действия в панелях становятся невозможными, но зато открываются самые широкие возможности манипулирования непосредственно командами оболочки. После чего становится ясным, почему по умолчанию панели deco свернуты на половину экрана (что, впрочем, легко изменить - включив через меню или комбинацией Control+F режим Full Screen: нижняя его часть, в сущности, представляет собой обычное терминальное окно.

Сам по себе deco предлагает на выбор одну из двух встроенных командных оболочек - /bin/sh и /bin/csh. Правда, интерактивные их возможности очень бедны: не поддерживаются ни пролистывание истории команд (хотя таковая само по себе и поддерживается), ни автодополнение, ни псевдонимы, ни прочие давно уже привычные радости. Попытки настройки оболочек также остаются безуспешными. по крайней мере, никакие мои манипуляции с профильными файлами (типа ~/.csh или ~/.profile) эффекта не возымели ни малейшего. Честно говоря, для меня так и осталось загадкой, откуда deco черпает сведения о переменных оболочки и окружения.

Тем не менее, работа в режиме командной строки оказывается весьма комфортной. При этом начинаешь понимать, что программа эта, в сущности, никакой не файловый менеджер; или, мягче сказать, эта ее функция - лишь дополнение к основной, а именно: визуализации действий в командной строке. То есть именно то, что восхитило меня в свое время в Konqueror'е - файловом менеджере-браузере из KDE. Кстати сказать, в экранной документации deco так и называется - visual shell, а отнюдь не файловый менеджер.

Конечно, хотелось бы, чтобы терминальное окно под визуализационными панелями использовалось наиболее эффективно. То есть - подключить к deco какую-либо мощную современную командную среду (типа bash, tcsh или zsh). Однако возможности для этого не предусмотрено: так называемые /bin/sh и /bin/csh встраиваются в него статически, и изменить это мне не удалось.

К сожалению, ожидать совершенствования deco не приходится: судя по всему, развитие программы прекратилось во второй половине 90-х годов. Однако сам по себе факт ее существования вселяет надежду: а вдруг кому придет в голову создать аналогичный визуализатор командных оболочек, но уже на современном этапе их развития?

Утилита find как универсальный файловый инструмент

2001-2005 гг

В этой заметке речь пойдет о наборе исходников, известном в проекте GNU как findutils. И в первую голову - о команде find (как, впрочем, и о тесно связанной с ней команде xargs). Столь высокая честь выпадает им потому, что посредством этих двух команд можно решить если не все, то большинство (Buono Parte) проблем, возникающих при работе с файлами.

Кроме find и xargs, в состав набора findutils входят команды locate и updatedb, о которых говорилось ранее, а также команды bigram, code, frcode, реального приложения которых я, честно говоря, не знаю (и, соответственно, говорить о них не буду).

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

$ whatis find
find(1) - walk a file hierarchy

что применительно случаю можно перевести как "прогулка по файловой системе".

Команда find по своему синтаксису существенно отличается от большинства прочих Unix-команд. В обобщенном виде формат ее можно представить следующим образом:

$ find аргумент [опция_поиска] [значение] \
[опция_действия]

Аргумент - это путь поиска, то есть каталог, начиная с которого следует искать файлы, например, корневой

$ find / [опция_поиска] [значение] \
[опция_действия]

или домашний каталог пользователя

$ find ~/ [опция_поиска] [значение] \
[опция_действия]

Опция поиска - критерий, по которому следует искать файл (файлы). В качестве таковых могут выступать имя файла (-name), его тип (-type), атрибуты принадлежности, доступа или времени.

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

Как можно заметить, опция поиска и опция действия предваряются знаком дефиса, значение первой отделяется от ее имени пробелом.

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

  • name - поиск по имени файла или по маске имени; в последнем случае метасимволы маски должны обязательно экранироваться (например, - name *.tar.gz) или заключаться в кавычки (одинарные или двойные, в зависимости от правил, принятых для данной командной оболочки; этот критерий чувствителен к регистру, но близкий по смыслу критерий iname позволяет производить поиск по имени без различения строчных и заглавных букв;
  • type - поиск по типу файла; этот критерий принимает следующие значения - f (регулярный файл), d (каталог), s (символическая связь), b (файл блочного устройства), c (файл символьного устройства);
  • user и group - поиск по имени или идентификатору владельца или группы, выступающим в качестве значения критерия; существует также критерии nouser и nogroup - они отыскивают файлы, владельцев не имеющие (то есть тех, учетные записи для которых отсутствую в файлах /etc/passwd и /etc/group); эти критерии в значения, разумеется, не нуждаются;
  • size - поиск по размеру, задаваемому в виде числа в блоках или в байтах - в виде числа с последующим символом c; возможны значения n (равно n блоков), +n (более n блоков), -n (менее n блоков);
  • perm - поиск файлов по значениям их атрибутов доступа, задаваемых в символьной форме;
  • atime, ctime, mtime - поиск файлов с указанными временными атрибутами; значения временных атрибутов указываются в сутках (точнее, в периодах, кратных 24 часам); возможны формы значений этих атрибутов: n (равно указанному значению n*24 часа), +n (ранее n*24 часа), -n (позднее n*24 часа);
  • newer - поиск файлов, измененных после файла, указанного в качестве значения критерия (то есть имеющего меньшее значение mtime);
  • maxdepth и mindepth позволяют конкретизировать глубину поиска во вложенных подкаталогах - меньшую или равную численному значению для первого критерия и большую или равную - для второго;
  • depth - производит отбор в обратном порядке, то есть не от каталога, указанного в качестве аргумента, а с наиболее глубоко вложенных подкаталогов; смысл этого действия - получить доступ к файлам в каталоге, для которого пользователь не имеет права чтения и исполнения;
  • prune - позволяет указать подкаталоги внутри пути поиска, в которых отбора файлов производить не следует.

Кроме этого, существует еще одна опция поиска - fstype, предписывающая выполнять поиск только в файловой системе указанного типа; очевидно, что она может сочетаться с любыми другими опциями поиска. Например, команда

$ find / -fstype ext3 -name zsh*

будет искать файлы, имеющие отношение к оболочке Z-Shell, начиная с корня, но только - в пределах тех разделов, на которых размещена файловая система Ext3fs (на моей машине - это именно чистый корень, за вычетом каталогов /usr, /opt, /var, /tmp и, конечно же, /home.

Критерии отбора файлов могут группироваться практически любым образом. Так, в форме

$ find ~/ -name *.tar.gz newer filename

она выберет в домашнем каталоге пользователя все компрессированные архивы, созданные после файла с именем filename. По умолчанию между критериями отбора предполагается наличие логического оператора "И". То есть будут отыскиваться файлы, удовлетворяющие и маске имени, и соответствующему атрибуту времени. Если требуется использование оператора "ИЛИ", он должен быть явно определен в виде дополнительной опции -o между опциями поиска. Так, команда:

$ find ~/ -mtime -2 -o newer filename

призвана отобрать файлы, созданные менее двух суток назад, или же - позднее, чем файл filename.

Особенность GNU-реализации команды find (как, впрочем, и ее тезки из числа BSD-утилит) - то, что она по умолчанию выводит список отобранных в соответствии с заданными критериями файлов на экран, не требуя дополнительных опций действия. Однако, как говорят, в других Unix-системах (помнится, даже и в некоторых реализациях Linux мне такое встречалось) указание какой-либо из таких опций - обязательно. Так что рассмотрим их по порядку.

Для выведения списка отобранных файлов на экран в общем случае предназначена опция -print. Вывод этот имеет примерно следующий вид:

$ find . -name f* -print
./file1
./file2
./dir1/file3

Сходный смысл имеет и опция -ls, однако она выводит более полные сведения о найденных файлах, аналогично команде ls с опциями -dgils:

$ find / -fstype ext3 -name zsh -ls
88161 511 -rwxr-xr-x 1 root root 519320 Ноя 23 15:50 /bin/zsh

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

find: /root: Permission denied

указывающие на каталоги, закрытые для просмотра обычным пользователем, и весьма мешающие восприятию. Чтобы подавить их, следует перенаправить вывод сообщения об ошибках в файл /dev/null, то есть указать им "Дорогу никуда":

$ find / -fstype ext3 -name zsh -ls 2> /dev/null

Идем далее. Опция -delete уничтожит все файлы, отобранные по указанным критериям. Так, командой

$ find ~ -atime +100 -delete

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

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

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

$ find ~/ -atime +100 -exec rm -i {} \;

В этом случае на удаление каждого отобранного файла будет запрашиваться подтверждение.

Обращаю внимание на последовательность символов {} \; (с пробелом между закрывающей фигурной скобкой и обратным слэшем) в конце строки. Пара фигурных скобок {} символизирует, что свои аргументы исполняемая команда (в примере - rm) получает от результатов отбора команды find, точка с запятой означает завершение команды-значения опции -exec, а обратный слэш экранирует ее специальное значение от интерпретации командной оболочкой.

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

Приведенный пример, хотя и вполне жизненный, достаточно элементарен. Рассмотрим более сложный случай - собирание в один каталог всех скриншотов в формате PNG, разбросанных по древу домашнего каталога:

$ find ~/ -name *.png -exec cp {} imagesdir \;

В результате все png-файлы будут изысканы и скопированы (или - перемещены, если воспользоваться командой mv вместо cp) в одно место.

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

Зачем и отчего это нужно? Поясню на примере. Как-то раз, обзаведясь огромным по тем временам (40 Гбайт) винчестером, я решил собрать на него все нужные мне данные, рассеянные по дискам CD-R/RW (суммарным объемом с полкубометра) и нескольким сменным винчестерам, одни из которых были отформатированы в FAT16, другие - в FAT32, третьи - вообще в ext2fs (к слову сказать, рабочей моей системой в тот момент была FreeBSD). Сгрузив все это богачество в один каталог на новом диске, я создал в нем весьма неприглядную картину.

Ну, во-первых, все файлы, скопированные с CD и FAT-дисков, получили (исключительно из-за неаккуратности монтирования, с помощью должных опций этого можно было бы избежать, но - спешка, спешка...) биты исполняемости, хотя все это были файлы данных. Казалось бы, мелочь, но иногда очень мешающая; в некоторых системах это не позволяет, например, просмотреть html-файл в Midnight Commander простым нажатием Enter. Во-вторых, для некоторых каталогов, напротив, исполнение не было предусмотрено ни для кого - то есть я же сам перейти в них не мог. В третьих, каталоги (и файлы) с CD часто не имели атрибута изменения - а они нужны мне были для работы (в т.ч. и редактирования). Кончено, от всех этих артефактов можно было бы избавиться, предусмотрев должные опции монтирования накопителей (каждого накопителя - а их число, повторяю, измерялось уже объемом занимаемого пространства), да я об этом и не подумал - что выросло, то выросло. Так что ситуация явно требовала исправления, однако проделать вручную такую работу над данными более чем в 20 Гбайт виделось немыслимым.

Да так оно, собственно, и было бы, если б не опция -exec утилиты find. Каковая позволила изменить права доступа требуемым образом. Итак, сначала отбираем все регулярные файлы и снимаем с них бит исполнения для всех, заодно присваивая атрибут изменения для себя, любимого:

$ find ~/dir_data -type f \
-exec chmod a-x,u+w {} \;

Далее - поиск каталогов и обратная процедура над итоговой выборкой:

$ find ~/dir_data -type d \
-exec chmod a+xr,u+w {} \;

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

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

$ tar cvf alldata.tar ~/*

А затем в меру своей испорченности (или, напротив, аккуратности), время от времени запускаем команду

$ find ~/ -newer alldata.tar \
-exec tar uvf alldata.tar {} \;

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

А пока - об ограничении возможностей столь замечательной сцепки команды find с опцией действия -exec (распространяющиеся и на опцию -ok). Оно достаточно очевидно: вызываемая любой из этих опций команда выполняется в рамках самостоятельного процесса, что на слабых машинах, как говорят, приводит к падению производительности (должен заметить, что на машинах современных заметить этого практически невозможно).

Тем не менее, ситуация вполне разрешима. И сделать это призвана команда xargs. Она определяется как построитель и исполнитель командной строки со стандартного ввода. А поскольку на стандартный ввод может быть направлен вывод команды find - xargs воспримет результаты ее работы как аргументы какой-либо команды, которую, в свою очередь, можно рассматривать как аргумент ее самоё (по умолчанию такой командой-аргументом является /bin/echo).

Использование команды xargs не связано с созданием изобилия процессов (дополнительный процесс создается только для нее самой). Однако она имеет другое ограничение - лимит на максимальную длину командной строки. Во FreeBSD, например (по крайней мере, в системе, на которой я нынче редактирую этот текст) этот лимит составляет 65536, что определяется командой следующего вида:

sysctl -a | grep kern.argmax

И способы изменить его мне не известны - был бы благодарен за соответствующую информацию.

Поэтому в реальности у меня не возникало необходимости в команде xargs и, соответственно, я не занимался ее глубоким изучением. Так что заинтересованных отсылаю к соответствующей man-странице.