Брайан Керниган - UNIX — универсальная среда программирования
В большинстве программ, которые ожидают текст в качестве входного потока, существуют ограничения реализации. Мы проверили несколько программ на тексте размером 30 тыс. байт, не содержащем ни одного символа перевода строки, и только некоторые из них работали правильно, поскольку многие программы делают явно неоговариваемые допущения о максимальном размере строки текста (исключительные ситуации см. в разделе BUGS (ошибки) описания sort(1)).
Нетекстовые файлы, несомненно, имеют свою специфику. Например, для очень больших баз данных обычно нужна дополнительная адресная информация, обеспечивающая быстрый доступ; для повышения эффективности она должна быть представлена в двоичном виде. Но каждому формату файла, не являющемуся текстовым, должно соответствовать свое семейство программ, выполняющее те операции, которые для текстовых файлов производят стандартные средства. Машинная обработка текстовых файлов, возможно, несколько менее эффективна, но это уравновешивается стоимостью дополнительного программного обеспечения, поддерживающего более сложные форматы. Если вы выбираете формат файла, следует тщательно все продумать, прежде чем остановиться на нетекстовом представлении. (Желательно также предусмотреть вариант, при котором поведение вашей программы будет "осмысленным" в случае длинных входных строк.)
2.3. Каталоги и имена файлов
Все ваши файлы имеют вполне определенные имена, начиная с /usr/you, но если у вас есть только файл junk, то при задании команды ls не выдается /usr/you/junk; имя файла выводится без всякого префикса:
$ ls
junk
$
Это происходит потому, что любая выполняемая программа, т.е. каждый процесс, имеет текущий каталог и неявно предполагается, что все имена файлов начинаются с имени этого каталога, если они явно не начинаются с дробной черты. Таким образом, у интерпретатора shell, в который вы вошли, и команды ls есть текущий каталог. Команда pwd ("print working directory" печать текущего каталога) выдает имя текущего каталога:
$ pwd
/usr/you
$
Текущий каталог представляет собой атрибут процесса, а не пользователя или программы: пользователям соответствуют начальные каталоги, а процессам — текущие. Если процесс порождает процесс-потомка, последний наследует текущий каталог родителя. Но, если затем потомок сменит текущий каталог, родителя это не затронет: его текущий каталог останется тем же, независимо от действий потомка.
Понятие текущего каталога, конечно, обеспечивает удобство обозначения, поскольку освобождает от излишнего ввода, но настоящее его назначение — организационное. Связанные друг с другом файлы находятся в одном каталоге. Каталог /usr обычно является начальным каталогом файловой системы пользователей (usr — сокращение от user, подобно cmp, ls и т.д.). Ваш начальный каталог /usr/you — это ваш текущий каталог при первом вхождении в систему. Каталог /usr/src содержит исходные тексты системных программ, каталог /usr/src/cmd — исходные тексты команд UNIX, /usr/src/cmd/sh — исходные тексты интерпретатора shell и т.д. Всякий раз, когда вы приступаете к новому проекту или когда у вас появляется группа связанных файлов, скажем, набор рецептов, вы можете создать новый каталог с помощью команды mkdir и поместить в него файлы.
$ pwd
/usr/you
$ mkdir recipes
$ cd recipes
$ pwd
/usr/you/recipes
$ mkdir pie cookie
$ ed pie/apple
...
$ ed
...
$
Заметьте, как легко ссылаться на вложенные каталоги. Файл pie/apple имеет очевидный смысл: рецепт яблочного пирога из каталога /usr/you/recipes/pie. Вместо этого вы могли бы поместить рецепт, например, в каталог recipes/apple.pie, а не во вложенный каталог в каталоге recipes, но лучшее решение — собрать все рецепты пирогов вместе. Так, рецепт крема мог бы храниться в recipes/pie/crust, чтобы не дублировать его в рецепте для каждого пирога. Хотя файловая система предоставляет мощное средство организации данных, вы можете забыть, куда помещен файл, и даже какие файлы у вас есть. Естественным решением было бы иметь одну или несколько команд, позволяющих "порыться" в каталогах. Конечно, команда ls помогает искать файлы, но не дает возможности исследовать вложенные каталоги:
$ cd
$ ls
junk
recipes
$ file *
junk: ascii text
recipes: directory
$ ls recipes
cookie
pie
$ ls recipes/pie
apple
crust
$
Эту часть файловой системы можно изобразить графически:
Рис. 2.1: Часть файловой системы
С помощью команды du ("disk usage" — использование диска) вы можете выяснить, какое пространство на диске занято файлами каталога, включая все вложенные каталоги:
$ du
6 ./recipes/pie
4 ./recipes/cookie
11 ./recipes
13 .
$
Смысл имен файлов понятен; числа соответствуют количеству блоков на диске (обычно размер блока составляет 512 или 1024 байта) для хранения каждого файла. При использовании каталога число показывает, сколько блоков задействовано всеми файлами этого каталога, включая вложенные каталоги и сам каталог.
Команда du имеет флаг -a ("all" — все), который означает, что требуется выдавать все файлы в каталоге. Если один из них является каталогом, команда du сообщает и о нем:
$ du -а
2 ./recipes/pie/apple
3 ./recipes/pie/crust
6 ./recipes/pie
3 ./recipes/cookie/choc.chip
4 ./recipes/cookie
11 ./recipes
1 ./junk
13 .
$
Выходной поток команды du -a можно направить по программному каналу через команду grep для поиска каких-либо файлов:
$ du -a | grep choc
3 ./recipes/cookie/choc.chip
$
Напомним (см. гл. 1), что имя '.' — это запись в каталоге, обозначающая сам каталог; оно обеспечивает доступ к каталогу в тех случаях, когда не известно его полное имя. Команда du просматривает файлы в каталоге, причем если вы не указали, в каком именно каталоге следует производить поиск, то она выберет '.', т. е. каталог, с которым вы работаете в данный момент. Значит, junk и ./junk — имена одного и того же файла.
Несмотря на то, что каталоги играют в системе важную роль, они представляются в файловой системе как обычные файлы. Эти каталоги можно читать, но в отличие от традиционных файлов их нельзя создавать и в них нельзя писать. Для сохранения целостности системы и файлов пользователей ядро берет на себя контроль за содержимым каталогов.
Теперь представим содержимое каталога в байтовой форме:
$ od -cb
000000 4 ; .
064 073 056 000 000 000 000 000 000 000 000 000 000 000 000
000020 273 ( . .
273 050 056 056 000 000 000 000 000 000 000 000 000 000 000
000040 252 ; p е ц е п т ы
252 073 256 243 263 243 255 260 273 000 000 000 000 000 000
000060 230 = j u n k
230 075 152 165 156 153 000 000 000 000 000 000 000 000 000
000100 $
Видите имена файлов, "спрятанные" здесь? Формат каталога — это комбинация двоичного и текстового представлений. Каталог строится из фрагментов по 16 байт, причем последние 14 байт здесь содержат имя файла, дополненное символом NUL из ASCII (нулевой код, имеющий значение 0), а первые два байта указывают системе, где находится служебная информация, относящаяся к файлу (мы вернемся к этому вопросу позднее). Каждый каталог начинается двумя записями: '.' (точка) и '..' (точка-точка).
$ cd Начальный каталог
$ cd recipes
$ pwd
/usr/you/recipes
$ cd ..; pwd На один уровень выше
/usr/you
$ cd ..; pwd Еще на один уровень выше
/usr
$ cd ..; pwd Еще на один уровень выше
/
$ cd ..; pwd Еще на один уровень выше
/ Выше некуда
$
Каталог / называется корнем файловой системы. Каждый файл системы находится в корневом каталоге или в одном из вложенных в него каталогов, и корневой каталог является родителем самому себе.
Упражнение 2.2На основании изложенного выше представьте приблизительно действие команды ls. Подсказка: cat . > foo; ls -f foo.
Упражнение 2.3(Более сложное) Как действует команда pwd?
Упражнение 2.4Команда du предназначена для учета использования дискового пространства. Осуществлять с ее помощью поиск файлов в иерархии каталогов — довольно странное решение, возможно, даже неподходящее. За альтернативой обратитесь к странице справочного руководства find(1) и сравните две команды, в частности du -a | grep ... и find. Какая из них сработает быстрее? Что лучше: создать новую команду или воспользоваться побочным эффектом уже существующей?