2.2.3. kind - вывод однотипных файлов


    ИМЯ:        kind

kind Выдача списка имен файлов определенного вида

НАЗНАЧЕНИЕ

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

ФОРМАТ ВЫЗОВА
    kind [-a] [-d] [-t] [-x] [file...]
ПРИМЕР ВЫЗОВА
    more `kind /etc/*`

Вывод командой more всех текстовых файлов, имеющихся в каталоге /etc.

ТЕКСТ ПРОГРАММЫ
   1   :
   2   # @(#) kind v1.0  Prints files of the same kind  Author: Russ Sage
   2а                      Выводит список файлов определенного вида
   
   4   if [ $# -gt 0 ]
   5     then if [ `echo $1 | cut -c1` = "-" ]
   6            then case #1 in
   7                 -a)  KIND='archive'
   8                      shift;;
   9                 -d)  KIND='data'
   10                     shift;;
   11                -t)  KIND='text'
   12                     shift;;
   13                -x)  KIND='executable'
   14                     shift;;
   15                 *)  echo "kind: arg error"                           >&2
   16                     echo "usage: kind [-a] [-d] [-t] [-x] [file...]" >&2
   17                     echo "       -a  archive"                        >&2
   18                     echo "       -d  data"                           >&2
   19                     echo "       -t  text, default"                  >&2
   20                     echo "       -x  executable"                     >&2
   21                     echo "       if no args, reads stdin"            >&2
   22                     exit 1;;
   23                esac
   24         fi
   25   fi
   
   27  : ${KIND:='text'}
   29  case $# in
   30  0)   while read FILE
   31       do
   32           file $FILE | fgrep $KIND | cut -d: -f1
   33       done;;
   34  *)   file $@ | fgrep $KIND | cut -d: -f1;;
   35  esac
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FILE Содержит имена файлов по мере их чтения из stdin (стандартного ввода)
KIND Содержит строку, определяющую тип файла
ОПИСАНИЕ

ЗАЧЕМ НУЖЕН КОМАНДНЫЙ ФАЙЛ kind?

Файловая система UNIX имеет строгие стандарты при рассмотрении файлов. Имеется три вида файлов: обычные файлы (тексты, данные, исполняемые файлы), каталоги и устройства. Каждый вид файлов имеет свое предназначение и обычно имеет особые команды или файлы данных, которые работают с ним.

Давайте рассмотрим, как некоторые из существующих команд системы UNIX рассматривают типы файлов. Команда ls делает различие между каталогами и другими файлами, поэтому она может быть полезна. Другой важной командой является команда file. Она сообщает вам, какой тип имеет данный файл, что потенциально полезно, но слишком мало. Для того чтобы извлечь из команды file какую-либо полезную информацию, вы должны надстроить над ней некоторую программу. Что нам действительно нужно, так это некий гибрид команд ls и file, т.е. утилита, которая выводит имена всех файлов указанного типа.

Примером полезности такого рода утилиты может служить анализ содержимого каталогов. Возьмем, например, каталог /etc. Он содержит программы, файлы данных и текстовые файлы. Для каждого из этих типов требуется свой собственный тип анализа. Программы анализируются командами ls, size, nm и file. Файлы данных анализируются командой od.

Текстовые файлы анализируются командами more, wc, head, tail и другими. Таким образом, обычно вам необходимо работать в данный момент времени с файлами какого-нибудь одного типа.

ЧТО ДЕЛАЕТ kind?

Командный файл kind - это утилита, которая распечатывает имена всех файлов, имеющих указанный тип. Она имеет интерфейс, похожий на интерфейс команды ls, т.е. вы можете передать ей опции и имена файлов или символы расширения имен файлов. При выводе отображаются полные имена, если они были указаны, но вы можете избежать появления лишней информации при выводе, если предварительно перейдете в нужный каталог с помощью команды cd. Например, если бы я находился в моем регистрационном каталоге (/usr/russ) и ввел команду

      $ kind -d /etc/*
   то вывод мог бы выглядеть так:
   |
   |   /etc/mnttab
   |   /etc/utmp
   |   /etc/wtmp
   |

То есть, вывелся список всех файлов данных. А если бы я выполнил такую последовательность команд:

      $ cd /etc
      $ kind -d *

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

   |
   |    mnttab
   |    utmp
   |    wtmp
   |

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

Допустимыми опциями команды kind являются -a для файлов архивов, -d для файлов данных, -t для текстовых файлов (что является умолчанием) и -x для исполняемых файлов. Определение этих типов соответствует команде UNIX file. Заметим, что критерии того, что файл является исполняемым, в команде file отличаются от тех, которые применяет команда ls: ls проверяет биты x в индексном дескрипторе файла, в то время как file проверяет, являются ли первые несколько байтов содержимого файла "магическим числом". Это магическое число является идентификатором структуры a.out (см. /usr/include/a.out.h), который сообщает "Я являюсь скомпилированной Си-программой".

Имена файлов появляются в командной строке после опций. Эти имена могут быть порождены любым стандартным методом системы UNIX. Если в командной строке нет имен файлов, то kind превращается в фильтр и читает стандартный ввод для получения списка имен файлов. (Обратите внимание, что я сказал "имен файлов", а не "файлов". Можно использовать опции, поскольку они убираются из командной строки командой shift по мере того, как они встречаются.) Таким образом, вы можете использовать другие команды для того, чтобы передать по конвейеру список файлов утилите kind. Она отфильтровывает и выводит только те из них, которые соответствуют нужному вам типу.

ПРИМЕРЫ
    1.   $ od `kind -d /etc/*`

Выглядит так, как будто это должно работать, но команда od не работает с набором имен файлов. Она может обрабатывать только один файл в данный момент времени.

    2.   $ ll `sh -x kind -a /lib/*` | m

Это длинный пример. Выводит в длинном формате список всех файлов архивов, которые находятся в каталоге /lib. Мы запускаем shell в отладочном режиме выполнения, так что вы можете увидеть каждую командную строку перед ее выполнением. Результат по конвейеру передается команде more.

   3.   # find / -print | kind -x | while read FILE
   > do
   >        ll $FILE
   > done > /tmp/filelist

Данный цикл обнаруживает все действительно исполняемые файлы. Для каждого из них выполняется команда "ls -l". Отметим, что здесь команда ll вызывается для каждого имени файла.

Вы могли бы выполнить ту же операцию при помощи такого оператора find:

    find / -perm -0111 -exec ll {} \;

но опция perm в данном случае опять же проверяет биты прав доступа в индексном дескрипторе файла, а не ищет магическое число в структуре a.out, как описано ранее. Кстати, для того, чтобы вы могли успешно запустить команду file (и тем самым kind) на системных файлах, вы должны иметь права чтения, чтобы можно было прочитать магическое число.

   4. $ for F in `kind /bin/* /usr/bin/* /etc/*`
      > do
      >      fgrep "trap" $F /dev/null
      > done
      $ fgrep "trap" `kind /bin/* /usr/bin/* /etc/*`
   

$ find /bin /usr/bin/etc -exec fgrep "trap" {} \;

Это три различных способа поиска слова "trap" во всех текстовых файлах.

ПОЯСНЕНИЯ

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

В строке 4 проверяется, включены ли какие-либо параметры. Если параметры есть, то они обрабатываются. Если не используются никакие параметры, ничего не делается и управление передается на строку 27. Если были использованы какие-либо аргументы и первым символом является знак минуса (-), то выполняется оператор case для определения того, какой тип файла указан. Переменная KIND устанавливается в соответствии с типом файла, и данный параметр удаляется из командной строки командой shift. Если аргумент не совпадает ни с одной из допустимых опций, то ему соответствует случай *, что означает ошибку. На стандартное устройство регистрации ошибок выводится соответствующее сообщение об ошибке и синтаксическая подсказка, после этого kind завершается с плохим статусом выполнения.

В строке 27 производится проверка того, установлена переменная KIND или равна нулю. Если она равна нулю, в нее подставляется символьная строка "text". Если KIND уже установлена, то она не меняется. Это неплохой оператор присвоения значения по умолчанию. Таким образом, пользователь не обязан указывать опцию -t в командной строке. Если же опция -t была указана, то ей есть что сопоставить в операторе case.

Оставшаяся часть программы в строках 29-35 представляет собой еще один оператор case, который проверяет количество аргументов, оставшихся в командной строке после обработки ошибок. Если была указана какая-либо опция, то переменная KIND установлена и опция убрана командой shift. В командной строке могли остаться только аргументы, которые являются именами файлов или маршрутами. Если к тому времени, когда мы уже готовы к заключительной обработке, не осталось никаких аргументов, то значит в командной строке не было указано ни одного имени файла.

В этом случае в строках 30-33 организовывается цикл, который читает имена файлов из стандартного ввода, запускает команду file и использует команду fgrep для определения того, соответствует ли тип файла, выданный командой file, интересующему нас типу (хранимому в переменной KIND). Затем мы используем команду cut для выделения того, что нам нужно. Обычный вывод команды file содержит имя файла, двоеточие и затем описание. Нам нужно только имя файла, поэтому мы вырезаем первое поле, используя разделитель ":". Когда никакие данные больше не поступают, цикл while завершается, мы попадаем в конец оператора case и выходим из программы.

Если же аргументы НАЙДЕНЫ в командной строке, то вместо всего этого выполняется ветвь оператора case в строке 34. С помощью обозначения $@, имена всех файлов в командной строке включены в команду file. Таким образом, не нужен никакой цикл. Во всем остальном обработка идентична строке 32.

ВОЗМОЖНЫЕ МОДИФИКАЦИИ

Было бы неплохо, если бы командный файл kind мог работать одновременно с разными типами файлов. Это означает наличие несколько опций в командной строке, например -a и -d. Вам могла бы понадобиться составная строка, в которой каждая часть была бы отделена символом |. Затем эта строка могла бы быть использована в команде egrep, например, "egrep 'archive|data'". Вам пришлось бы организовать цикл по командной строке вместо использования фиксированных позиций и убедиться в том, что вы не получите зациклившийся конвейер, когда задана только одна опция.

2.2.4. m - простой доступ к команде more


    ИМЯ: m

m Простой доступ к команде more

НАЗНАЧЕНИЕ

Обеспечивает быстрый и простой способ постраничного вывода

ФОРМАТ ВЫЗОВА
    m [more options] [file ...]
ПРИМЕР ВЫЗОВА

m * Вывод командой more всех файлов текущего каталога

ТЕКСТ ПРОГРАММЫ
   1   :
   2   # @(#)  m v1.0   Easy access to more
   2а                   Простой доступ к команде more
   
   4   /usr/bin/more $@
ОПИСАНИЕ

Зачем нужен командный файл m?

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

Имеется два основных способа вывода данных на экран. Первый способ - непосредственный вызов команды, например, "more datafile". Вы направляете данные на экран самой командой. Второй способ - использовать какую-нибудь команду для получения данных, а затем в конце перехватить их командой more, например "od -c . | more". В обоих этих случаях мы вводим с клавиатуры много символов. Сделав так, чтобы команда more вызывалась по одному символу, мы могли бы уменьшить последние две команды на шесть нажатий на клавиши. За целый день это хоть немного предохранит клавиатуру от разрушения! (Если ваша система поддерживает вызов команд по псевдонимам (aliasing), то, как указывалось ранее, вы могли бы использовать в этом случае команду alias: "alias m more".)

Что делает m?

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

Все опции и аргументы передаются в командной строке. Вы можете указать опции команды more в командной строке команды m. Они передаются без изменений. Можно указать имена файлов. Если они указаны, команда more выводит их. В противном случае ожидается поступление данных со стандартного ввода. Таким образом, m может быть использована в качестве "перехватчика" или как фильтр, как и команда more.

Для тех, кто не слишком знаком с опциями команды more, отметим, что существуют две изящные возможности: 1) вход в редактор vi в том месте, где находится курсор при выводе командой more; 2) выход из more для запуска команды shell и возврат в то место, откуда вы вышли. Первая опция выполняется при нажатии клавиши "v" в строке состояния команды more. (То есть когда more отобразила полный экран текста и ждет продолжения.) Вторая опция запускается при вводе ":!cmd" или "!cmd".

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

Обычный сеанс работы выглядит так:

   |
   |    m `path termcap`           <-поиск таблицы описания тер-
   |                                 минала (termcap) и вывод
   |          .                      ее командой more
         .
         .
     --More--(5%)                  <-строка состояния more
     v                               vi /etc/termcap
     vi +210 /etc/termcap          <-командная строка для редак-
                                тора vi получена от more
         .
         .
     :q                            <-выход из vi
     --More--(5%)                  <-возврат в more
     :!sh                            порождение нового shell'а
     $ date                          запуск команды date
     Wed Apr 23 07:15:04 PST 1986
     $ ^d                          <-убрать порожденный shell
     --More--(5%)                  <-возврат в more
     :f                              распечатка имени файла,
                                выводимого командой more
     "/etc/termcap" line 54           выход команды f
     --More--(5%)
     f                             <-команда more для пропуска
                                полного экрана
      .skipping 23 lines
         .
         .
     --More--(9%)                  <-пропуск и выдача текста
     q                               выход из команды more
ПРИМЕРЫ
    1.  $ ll -R / | m

Начиная с корневого каталога (/), вывести в длинном формате (ll) все файлы (опция -a подразумевается в ll) всей системы (-R) и постранично распечатать на экран (| m).

    2.  $ m `path inittab rc passwd`

Обнаружить и вывести с помощью more системные файлы inittab, rc и passwd. Неприятность здесь заключается в том, что данный маршрут скорее всего относится к каталогу /bin/passwd, а не /etc/passwd (поскольку каталог /etc размещается в конце каталогов), а это означает, что вы можете попытаться вывести на экран исполняемый файл. В зависимости от того, какую из версий команды more вы запустили, это может привести к чему угодно начиная с сообщения команды more о том, что это был не текстовый файл, и заканчивая тем, что ваш терминал начнет показывать непонятные символы и даже зависнет.

ПОЯСНЕНИЯ

Поскольку в этом командном файле не так много текста, то все довольно понятно, нет ни обработки ошибок, ни других дополнений. Просто нехитрый вызов команды more. Полное имя здесь указано с целью повышения быстродействия, как мы обсуждали ранее. Вы должны перепроверить местонахождение вашей команды more. В системе Berkeley она может находиться в каталоге /usr/ ucb/more. Воспользуйтесь командой path more для определения этого места и вставьте соответствующий маршрут вместо указанного нами.

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

    :.!path more

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

2.2.5. mmm - обработка программой nroff макрокоманд для рукописей


   ИМЯ:      mmm

mmm Командная строка nroff для макросов обработки рукописей

НАЗНАЧЕНИЕ

Вызывает текстовый процессор nroff со специальными опциями, которые инициализируют макросы обработки рукописей.

ФОРМАТ ВЫЗОВА
    mmm file [...]
ПРИМЕР ВЫЗОВА

mmm memo Обработать с помощью nroff файл моих заметок memo и отобразить его на экран

ТЕКСТ ПРОГРАММЫ
   1   :
   2   # @(#)  mmm v1.0 Nroff command line with mm macros Author: Russ Sage
   2а                   Командная строка nroff с макросами mm
   
   4   if [ "$#" -eq 0 ]
   5     then echo "mmm: wrong arg count"   >&2
   6          echo "usage: mmm file [...]"  >&2
   7          exit 1
   8   fi
   
   10  LIST=""
   11  for ARG in $*
   12  do
   13          if [ ! -f $ARG ]
   14            then echo "mmm: $ARG is not a regular file" >&2
   15            else LIST="$LIST $ARG"
   16          fi
   17  done
   
   19  nroff -r0O -mm $LIST
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
ARG Содержит каждый позиционный параметр командной строки
LIST Содержит список проверяемых имен файлов
ОПИСАНИЕ

Зачем нужен командный файл mmm?

Одним из фактов делового мира является работа с бумагами. Мы производим заметки, письма, контракты, документы, руководства и так далее. Если вы знакомы со стилем производства документации в системе UNIX, то ваши текстовые файлы в основном представлены в одном из форматов программы nroff.

Однако различные программы форматирования текстов служат различным целям. Имеется стандартный nroff и nroffс дополнениями, такими как макросы ms и mm. Для подготовки графической информации и выполнения типографских работ разработана программа troff. Система AT&T имеет целую программную среду под названием Writers Workbench, и система Berkeley имеет аналогичные возможности.

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

Использование заготовленных заранее команд означает, что мы можем делать полезную работу, даже если некоторое время мы не выполняли работу определенного вида. Мы также можем избежать многократных нажатий на клавиши. Мастера UNIX'а периодически уединяются в своих горных убежищах, где штудируют справочные руководства в поисках полезных, но доселе незамеченных опций, которые могут быть встроены в программные средства для повседневной работы. Если слишком некритично полагаться на ваш текущий набор инструментальных средств, то можно пропустить полезные возможности.

Что делает mmm?

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

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

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

ПРИМЕРЫ
    1.  $ mmm nroffile | m

Запуск команды nroff применительно к файлу nroffile, вывод результата на экран с передачей по конвейеру команде more. Это полезно при изучении утилиты nroff, проведении экспериментов с различными командами и наблюдения за соответствующими результатами.

   2.  $ for F in proj.?
       do
          mmm $F > $F.rf
       done

Обработка в цикле всех файлов, имена которых содержат символьную строку "proj.", за которой следует один символ. Это могут быть proj.1, proj.2 и так далее по всему набору символов вплоть до proj.z, proj.{, proj.|, proj.} и proj.~, если считать, что у вас есть файлы, имена которых содержат эти символы. Каждый файл обрабатывается, и выход nroff перенаправляется в файл с таким же именем, дополненным символами .rf.

    3.  $ mmm status[12] | lpr -o5

Обработка командой nroff файлов status1 и status2. Выход в стандартный вывод передается по конвейеру программе lpr. Программа lpr является фильтром и принимает или имена файлов в командной строке, или непосредственно данные, передаваемые ей по конвейеру (но не то и другое сразу). Опция -o5 указывает lpr сместить страницу на 5 символов.

ПОЯСНЕНИЯ

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

Переменная LIST инициализируется нулевым значением в строке 10. Обычно переменные интерпретатора shell и так в начале равны нулю, но предварительная установка значения является хорошим стилем программирования.

Затем мы обрабатываем каждый аргумент командной строки в цикле (строки 11-17). Все аргументы должны быть именами файлов, поэтому каждый из них проверяется на то, существует ли он как обычный файл. Если это не файл, то в стандартный файл ошибок выводится сообщение об ошибке. Тем не менее программа не завершается. Не следует аварийно прекращать всю программу только потому, что нет указанного файла. Мы пропускаем его и идем до конца списка аргументов. Это особенно актуально, если данная команда используется как фоновая во время выполнения другой работы. Пользователь скорее согласится с тем, чтобы было выполнено побольше работы, чем не сделано вообще ничего. Это решение, принятое в данной программе, и вы можете изменить его, если оно не подходит в вашей ситуации.

Если имени соответствует допустимый файл, оно добавляется в список хороших имен файлов. Этот список становится главным списком для команды nroff.

После того как все аргументы проверены, мы в строке 9 строим и выполняем командную строку nroff.

Опция -rO0 для nroff указывает макросам обработки рукописей (пакету mm) установить регистр, который имеет дело с отступом текста, в состояние, соответствующее отступу в 0 символов. Это значит, что весь текст начинается с крайней левой позиции, т.е. выровнен слева. Путем проведения экспериментов я обнаружил, что левое выравнивание текста программой nroff и установка отступа для принтера дает наиболее надежный вывод на печать. В противном случае, если вы установите отступ текста в nroff и отступ в принтере, то может произойти настоящее столкновение, когда дело коснется вывода колонок в странице. Вы можете изменить это место, если ваши программы вывода или устройства печати ведут себя как-то иначе. Опция -mm указывает программе nroff просмотреть библиотеку макросов обработки рукописей, чтобы определить, используются ли какие-либо из них во входном документе. Эти макросы очень большие и требуют много времени центрального процессора. Если вам необходимо использовать их, то вам потребуется большой компьютер или компьютер, специально предназначенный для этой цели, чтобы добиться хорошего времени получения результата.

Последним аргументом является $LIST. В этой переменной находится строка имен файлов, разделенных пробелами. Эти имена помещаются в командную строку nroff. Можете быть уверенными, что в этом месте нет никаких ошибок.

ВОЗМОЖНЫЕ МОДИФИКАЦИИ

Поскольку все аргументы рассматриваются как имена файлов, то у нас нет способа передачи дополнительных команд пакету mm. Наличие такой возможности было бы желательным, поскольку при экспериментировании с командой nroff вам необходимо пробовать различные опции, чтобы увидеть, как они действуют. Было бы тяжелой работой выполнять редактирование текста mmm, чтобы добавить одноразовые опции, которые могут вам никогда не понадобиться или опции, которые вы должны постоянно менять.

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

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

2.2.6. pall - печать всех файлов в дереве


    ИМЯ:  pall

pall Распечатка всех файлов в дереве каталогов

НАЗНАЧЕНИЕ

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

ФОРМАТ ВЫЗОВА
    pall [-t|-d] directory
ПРИМЕР ВЫЗОВА
    pall /usr/lib

Выводит на печать постранично все текстовые файлы в каталоге /usr/lib

ТЕКСТ ПРОГРАММЫ
   1.  :
   2   # @(#) pall v1.0 Print all files in a tree Author: Russ Sage
   2а                   Печатает все файлы в дереве
   
   4   if [ $# -eq 0 -o $# -gt 2 ]
   5     then echo "pall: wrong argument count"  >&2
   6          echo "usage: pall [-t|-d] dir"     >&2
   7          echo " -t text (default)"         >&2
   8          echo " -d dev (.c,.h,.mk,.s)"     >&2
   9          exit 1
   10  fi
   
   12  NAME=""
   13  if [ `echo $1 | cut
   -c1` = "-" ]
   14   then case $1 in
   15        -t)     NAME=""
   16                shift;;
   17        -d)     NAME="-name \"*.[chms]*\""
   18                shift;;
   19         *)     echo "pall: invalid arg $1"    >&2
   20                echo "usage: pall [-t|-d] dir" >&2
   21                echo " -t text (default)"      >&2
   22                echo " -d dev (.c,.h,.mk,.s)"  >&2
   23                exit 1;;
   24        esac
   25  fi
   
   27  echo "creating output file: /tmp/lpr$$"
   
   29  eval find $1 -type f $NAME -print | sort | while read FILE
   30  do
   31          if file $FILE |
   32            egrep 'exec|data|empty|reloc|cannot
   open' >/dev/null 2>&1
   33          then continue
   34          else file $FILE > /dev/tty
   35               pr $FILE
   36          fi
   37  done >> /tmp/lpr$$
   
   39  echo "\nSend /tmp/lpr$$ to line printer (y/n): \c"
   40  read CMD
   41  if [ "$CMD" = "y" ]
   42    then lpr /tmp/lpr$$
   43  fi
ПЕРЕМЕННЫЕ СРЕДЫ ВЫПОЛНЕНИЯ
FILE Содержит имя каждого файла, который должен быть обработан в цикле while
NAME Содержит строку поиска для определения местонахождения указанных файлов
CMD Содержит команду выдачи результатов на принтер
ОПИСАНИЕ

Зачем нужен командный файл pall?

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

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

Для выполнения всего этого процесса вручную требуется, чтобы вы по команде cd переходили в каждый уровень дерева файлов, находили текстовые файлы, обрабатывали их (обычно командой pr системы UNIX или каким-либо другим текстовым процессором) и распечатывали их. После выполнения всей этой работы вы еще должны собрать все отдельные распечатки вместе в строго определенном порядке. Это большая работа, подверженная ошибкам со стороны человека. Почему бы не позволить машине выполнять эту работу? Сейчас мы имеем концепции и средства, необходимые для создания такой утилиты.

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

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

Например, мое значение ulimit равно 4096. Размер блока в данном случае равен 512, а не 1024. Максимальный размер файла равен 2097152. Вы можете вычислить это прямо с клавиатуры, как показано ниже:

   $ ulimit
   4096
   $ expr 4096 \* 512
   2097152

Этого значения достаточно для большинства случаев.

Что делает pall?

Командный файл pall предназначен для поиска указанных файлов, обработки их командой UNIX pr и сборки всех результатов в один файл. После того как все исходные файлы будут обработаны командой pr, вам будет задан вопрос о том, хотите ли вы поставить выводной файл в очередь на печать к принтеру. Результирующий файл сохраняется в каталоге /tmp, где его можно использовать для других целей или удалить.

Опциями pall являются -t и -d. Опция -t используется по умолчанию и нет необходимости ее указывать. Она предназначена для документации и указана в командной строке, чтобы более ясно показать действие, которое будет выполнено.

Если выбрана текстовая опция, ищутся все файлы в древовидной структуре и затем отбираются только текстовые файлы. Если указана опция разработки -d (development), то ищутся только файлы, связанные с разработкой программ. Затем эти файлы отфильтровываются с целью получения текстовых файлов. Считается, что к разработке программ относятся файлы, имена которых имеют вид *.c для исходных файлов на языке Си, *.h для включаемых файлов заголовков, *.mk для файлов построения программ (makefiles) и *.s для исходных файлов на ассемблере. Если требуются какие-либо другие шаблоны, то такие символы могут быть легко помещены в текст программы.

Прежде чем начнет выполняться поиск файлов, на экран выводится имя временного файла, чтобы вы знали, как обратиться к нему после завершения выполнения команды. Все результаты, полученные после обработки файлов, направляются в этот один файл. Командный файл pall также выводит на экран сообщения, когда он обрабатывает файлы. Вывод файлов выполняется в реальном времени по мере обработки файлов. Распечатка производится при помощи обычной команды UNIX'а file. По распечатке вы можете судить о том, файлы какого типа обрабатываются в данный момент.

Если какой-либо норовистый файл проскользнет, то вы знаете, где он размещен и какого он типа. Это делает отладку гораздо более простой.

Для файлов выполняется обработка, которая является действием по умолчанию команды pr. Она разбивает файл на страницы и ставит заголовок в начале каждой страницы. Заголовок содержит дату, имя файла и номер страницы. Нет никакого иного способа передать заголовок командному файлу pall во время его работы, поскольку он предполагает, что вы хотите знать имя каждого файла таким, как оно есть на диске. Изменение строки заголовка вызвало бы только неприятности и дополнительную работу.

Способ вызова pall влияет на формат имени файла в заголовке. Если вы вызвали pall, используя абсолютное имя каталога, то в распечатке используются полные маршрутные имена. Если вы вызвали pall с относительными маршрутными именами, то они и используются при выводе на печать. Внутри pall используется команда find системы UNIX. Команда find использует данные из командной строки, т.е. те, которые ввел пользователь. Выводимый заголовок изменяется в зависимости от того, что указано в командной строке, которую использует find. Если вы вызываете pall, используя следующую командную строку, то заголовок содержит полное маршрутное имя:

   |
   |    $ pall /usr/include
   |
   |    May 5 10:39 1986 /usr/include/a.out.h Page 1
   |              .
   |              .
   |              .
   |    May 5 10:39 1986 /usr/include/ar.h Page 1
   |              .
   |              .
   |              .

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

   |
   |    $ cd /usr/include
   |    $ pall .
   |
   |    May 5 10:39 1986 ./a.out.h Page 1
   |              .
   |              .
   |              .
   May 5 10:39 1986 ./ar.h Page 1
             .
             .
             .
ПРИМЕРЫ
    1.  $ pall /usr/include

Выводит ВСЕ файлы заголовков. Сюда включаются файлы заголовков в подкаталоге sys и во всех других каталогах, которые могут располагаться ниже каталога /usr/include. Это и есть причина, по которой был написан командный файл pall. Он создает один огромный листинг всех файлов заголовков в отсортированном порядке с печатью в заголовке страниц полного имени.

    2.  $ pall $HOME/src

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

ПОЯСНЕНИЯ

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

В строке 12 инициализируется переменная NAME. Это действие выполняется по умолчанию, поэтому данная строка дает возможность не указывать опцию в командной строке. Оператор if в строке 13 означает: "Если первым символом первого аргумента является дефис", то нужно проверить, какая это опция.

Если установлена опция -t, то переменная NAME инициализируется нулевым значением, что совпадает с действием по умолчанию, поэтому на самом деле ничего не меняется. Затем эта опция удаляется из командной строки.

Если установлена опция -d, то переменная NAME инициализируется для поиска символьной строки, соответствующей именам файлов программной разработки. Обратите внимание, что двойные кавычки внутри оператора экранированы, т.е. впереди них стоят символы наклонной черты. Командный интерпретатор shell воспринимает кавычки вокруг строки поиска на первой фазе синтаксического разбора без отмены присвоения, тем самым оставляя двойные кавычки последующей команде find.

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

В строке 27 выводится сообщение о том, какой файл содержит результаты работы. Сам этот оператор не создает файл. Он только печатает имя файла, который будет создан позже.

Строки 29-43 - это главный цикл всей программы. Оператор find должен быть повторно проанализирован командой eval, поскольку переменная NAME содержит нужные нам данные. Если бы не было команды eval, то подстановка символьных строк выполнялась бы неправильно. Обратите внимание, что переменной NAME не требуются кавычки в строке 24. Они уже есть в переменной NAME, так как были обработаны командой eval.

Оператор find находит только файлы типа f, или обычные файлы, т.е. не каталоги и не символьные или блочные устройства. Здесь под "обычными файлами" понимается, что они еще могут быть файлами данных или исполняемыми. Если значение переменной NAME равно нулю, оно не влияет на командную строку. Если NAME содержит символы файлов программной разработки, то они становятся частью команды find при выполнении команды eval. Это накладывает ограничения на шаблон поиска команды find. Когда файлы найдены, их имена выводятся в стандартный вывод.

Стандартный вывод по конвейеру передается команде sort, располагающей имена файлов по порядку. Это сильно помогает при сортировке горы выводной информации. Чтение кипы распечаток толщиной в один фут может свести с ума кого угодно.

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

Для каждого подходящего файла выполняется проверка в строках 31-36. Проверка начинается с запуска команды file. Выход file по конвейеру передается команде egrep, которая ищет тип файла, соответствующий набору нескольких выражений. Если какое-либо выражение подходит, то нам не нужно обрабатывать этот файл. Это не текстовый файл, и его нельзя вывести на принтер. Во многих случаях файлы данных содержат большое количество символов прогона формата, которые выталкивают страницу после каждой пары символов. Если вы не будете находиться рядом с принтером, когда печатаются такие файлы, то вы можете получить листинг, занимающий половину ящика бумаги, затратив целый лес на ненужную работу.

Нам не нужен выход команды egrep, а только ее статус возврата.

Если egrep обнаруживает одно из указанных ей выражений, она завершается со статусом успеха, или 0. Тем самым проверка if включает выполнение оператора then, который в данном случае выводит нас из конструкции if-then-else и продолжает цикл while, пропуская таким образом файл.

Если же egrep не обнаружила ни одну из указанных символьных строк, то выполнение продолжается с оператора else, который выполняет еще одну команду file и переадресовывает ее вывод на устройство с именем /dev/tty. Это универсальное имя устройства, которое гарантирует вам вывод на экран вашего терминала. UNIX обеспечивает, что указание /dev/tty обходит любые команды переадресации вывода, действующие в данный момент. Поскольку стандартный вывод уже переадресован для всего цикла while, то нам нужно попасть на устройство /dev/tty, чтобы вывод шел на экран терминала, а не в файл печати. Отображение на терминал имени обрабатываемого файла позволяет пользователю знать, какой файл будет добавлен к распечатке.

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

После того как все файлы обработаны, задается вопрос о том, хотите ли вы вывести результирующий файл на печать. Предлагается ответить на этот вопрос "да" (yes) или "нет" (no), однако в программе проверяется только положительный ответ (yes). Это значит, что нажатие любой клавиши трактуется как ответ "no", кроме клавиши "y", означающей "да". Ответ пользователя читается с клавиатуры, и проверяется, является ли он символом "y". Если да, то файл ставится в очередь на печать. Если нет, дальнейшая проверка не производится и командный файл завершается.

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

УПРАВЛЕНИЕ ВЫВОДНЫМИ ФАЙЛАМИ БОЛЬШИХ РАЗМЕРОВ

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

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

Например, рассмотрим каталог /usr/lib. Этот каталог содержит много файлов на первом уровне и много каталогов первого уровня. Если бы мы не обработали все файлы каталога /usr/lib за одну попытку, мы должны были бы пойти вниз по подкаталогам каталога /usr/lib. Попытки делать это вручную и запускать pall в каждом подкаталоге заняли бы много времени и могли бы привести к ошибкам с вашей стороны. Кроме того, pall допускает указание только одного имени каталога, что приведет к получению большого количества распечаток и к путанице при их сортировке.

Что же делать? Радикальным решением является увеличение значения ulimit. Вы можете сделать это либо с помощью программы на языке Си, использующей системный вызов ulimit, либо командой shell'а ulimit. Техника выполнения такой работы представлена в главе 7.

ВОЗМОЖНЫЕ МОДИФИКАЦИИ

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

Следующим местом, где могут быть сделаны дополнения, являются опции, которые может понимать pall. Вам могут понадобиться файлы с определенными именами или определенными типами, например, файлы nroff. Эти опции могут быть легко добавлены в оператор case, что улучшит команду.

Последним местом возможных изменений является тип файлов, которые нужно пропускать. Символьная строка для команды egrep покрывает большинство важных нетекстовых типов файлов. В вашей системе могут быть какие-то особые типы или же имена могут быть другими. Если вам необходимо дополнить строку, сделайте это. Команда egrep может обработать довольно много информации. Я не знаю ее ограничений. Возможно, вы обнаружите их, просматривая исходный текст утилиты egrep. Если получается слишком длинная строка и не помещается на экране, ничего страшного. Перенос на следующие строки экрана не опасен, пока общее количество символов не превысит 255. Опасно только указывать переадресацию символьной строки if на нулевое устройство в следующей после команды egrep строке. Кажется, что все работает правильно, но это не так. Переадресация должна указываться в той же строке, где стоит команда egrep.

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

Назад | Содержание | Вперед