Программирование в стандарте POSIX

Организация файловой системы

Разбить на страницы
Показывать лекцию целиком

Основные понятия.

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

В стандарте зафиксированы следующие типы файлов:

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

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

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

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

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

    Сокет - это оконечная точка межпроцессных коммуникаций.

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

    Согласно стандарту POSIX-2001, для каждого процесса определен корневой каталог с именем / - вершина графа, из которой осуществляется доступ к прочим файлам данной файловой системы . "Абсолютного", единого для всех корня может и не существовать.

    В пределах файловой системы каждый файл имеет уникальный идентификатор ( порядковый номер - он же номер описателя файла ).

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

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

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

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

    Под маршрутным именем понимается цепочка символов, идентифицирующая файл и состоящая из нескольких (в том числе из нуля) простых имен файлов, разделенных символами / и называемых компонентами маршрута.   Маршрутные имена, начинающиеся символом /, называются абсолютными, они задают маршрут от корня файловой системы . (Отметим, что стандарт POSIX-2001 определяет трактовку маршрутных имен, начинающихся с двух символов /, как зависящую от реализации. В частности, таким способом в некоторых ОС задаются имена файлов на других хостах сети.)

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

    Маршрутным префиксом называется маршрутное имя, ссылающееся на каталог . Если маршрутное имя оканчивается символом /, оно обязано быть маршрутным префиксом.

    Маршрутные имена называют также составными   именами файлов . Примеры составных имен:

    /.autofsck
    /usr/lib/tcl
    ../susv3
    ./l4.txt

    Первые два имени являются абсолютными. файл   .autofsck лежит в корневом каталоге, файл   tcl - в подкаталоге lib подкаталога usr   корневого каталога. Третье и четвертое - относительные имена, специфицирующие, соответственно, файл   susv3, расположенный в иерархии на один уровень выше файлов   текущего каталога, и файл   l4.txt из текущего каталога. Последнее маршрутное имя можно было задать и проще:

    l4.txt

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

    Согласно стандарту, с каждым файлом ассоциирована по крайней мере следующая служебная информация:

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

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

    По отношению к конкретному файлу все пользователи делятся на три категории:

  • владелец файла ;
  • члены владеющей группы;
  • прочие пользователи.
  • Для каждой из них режим доступа определяет права на операции с файлом, а именно:

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

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

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

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

    Получение информации о файлах и файловых системах

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

    pwd  [-L | -P]

    #include <unistd.h>
    char *getcwd (char *buf, size_t size);

    Команда pwd с (подразумеваемой) опцией -L извлекает имя текущего каталога из переменной окружения PWD, если это возможно. Посредством опции -P выполняется раскрытие символьных ссылок: в выдаваемом на стандартный вывод абсолютном маршрутном имени вместо символьных ссылок подставляется их содержимое.

    Функция getcwd() помещает абсолютное маршрутное имя   текущего каталога (без символьных ссылок) в массив buf длины size, который и возвращается в качестве результата (при ошибке результат равен NULL).

    Приведем пример программы, использующей функцию getcwd() (см. листинг 4.2).

    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    int main (void) {
     size_t size;
     char *buf;
     char *apath;
     /* Выясним, каким должен быть размер буфера            */
     /* для абсолютного маршрутного имени текущего каталога */
     size = (size_t) pathconf (".", _PC_PATH_MAX);
     if ((buf = (char *) malloc (size)) == NULL) {
       fprintf (stderr, "\nНе удалось выделить буфер размера %d\n", size);
       return 1;
     }
      if ((apath = getcwd (buf, size)) == NULL) {
       fprintf (stderr, "\nНе удалось определить абсолютное маршрутное имя текущего каталога\n");
       return 1;
     }
     printf ("\nАбсолютное маршрутное имя текущего каталога: %s\n", apath);
     return 0;
    }

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

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

    ls  [опция ...] [файл ...]

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

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

    ls -l /

    может быть выдано следующее (см. листинг 4.3):

    total 338
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 a
    drwxr-xr-x    2 root     root         4096 Jul  8 13:32 bin
    drwxr-xr-x    4 root     root         1024 Jul  6 15:06 boot
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 cdrom
    drwxr-xr-x   18 root     root        86016 Sep  2 10:42 dev
    drwxr-xr-x    6 root     root         4096 Feb 23  2003 dss
    drwxr-xr-x   60 root     root         4096 Sep  2 10:42 etc
    drwxr-xr-x   20 root     root         4096 Jul 15 18:00 home
    drwxr-xr-x    2 root     root         4096 Jun 12  2001 initrd
    drwxr-xr-x    8 root     root         4096 Aug  8 13:16 lib
    drwx------    2 root     root        16384 Jul  6 14:15 lost+found
    drwxr-xr-x    2 root     root         4096 Apr  1  2002 misc
    drwxr-xr-x    5 root     root         4096 Jul  6 11:24 mnt
    drwxr-xr-x    7 root     root         4096 Jul 23 13:09 opt
    dr-xr-xr-x   84 root     root            0 Sep  2  2003 proc
    drwxr-x---    5 root     root         4096 Sep  1 17:10 root
    drwxr-xr-x    2 root     root         4096 Jul 11 17:38 sbin
    drwxrwxrwt   23 root     root         8192 Sep  2 12:43 tmp
    drwxr-xr-x   14 root     root         4096 Jul 15 12:50 usr
    drwxr-xr-x   16 root     root         4096 Jul  9 16:22 var

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

  • d - каталог ;
  • b - блочный специальный файл ;
  • c - символьный специальный файл ;
  • l - символьная ссылка ;
  • p - канал ;
  • - (минус) - обычный файл.
  • Отметим, что стандартом POSIX-2001 не предусмотрено обозначение для сокетов, указано только, что реализации могут вводить свои типы файлов и обозначения для них.

    Девять последующих символов отражают режим доступа к файлу: первые три символа - права доступа его владельца, следующие три - владеющей группы, последние три - права доступа прочих пользователей. Наличие буквы r ( чтение ), w ( запись ) или x (выполнение) означает, что соответствующее право имеется; знак минус свидетельствует об отсутствии права. Например, файл   /usr является каталогом, куда может писать только пользователь root, а читать и искать информацию - все остальные.

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

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

    Наконец, следуют дата и время последнего изменения и имя файла. По умолчанию имена файлов сортируются в алфавитном порядке.

    Опишем еще несколько употребительных опций служебной программы ls.

    Ключ Описание
    -a Вывести список всех файлов (обычно не выводятся данные о файлах, имена которых начинаются с точки).
    -C Выдавать имена файлов в несколько столбцов (с сортировкой по столбцам). Отметим, что если в командной строке встречаются пары взаимоисключающих опций (например, -lC), то действует та, что указана последней.
    -d Трактовать каталоги наравне с файлами других типов. Часто используется с опцией -l для получения сведений о состоянии каталога.
    -F Выводить символ / после имен каталогов, * - после выполнимых файлов, | - после каналов, @ - после символьных ссылок.
    -i Выдавать порядковый номер файла в файловой системе (см. выше).
    -R Рекурсивно обойти встретившиеся подкаталоги.
    -r Изменить порядок сортировки файлов на обратный.
    -t Использовать в качестве первичного ключа сортировки время последнего изменения (сначала идут самые свежие файлы ); имя служит вторичным ключом. Обычно символьные ссылки трактуются утилитой ls наравне с файлами других типов, только на месте имени выводится комбинацияимя_файла-ссылки -> содержимое_файла-ссылки. Для получения информации о файле, на который указывает символьная ссылка, следует воспользоваться одной из следующих опций.
    -L Если символьная ссылка является аргументом утилиты ls или встретилась в процессе обхода файловой иерархии, выдавать информацию об указуемом файле, а не о ссылке. На месте имени выводится имя файла -ссылки (а не указуемого файла ).
    -H Аналогична -L, но воздействует только на символьные ссылки, заданные в командной строке и указывающие на каталог.

    Приведем еще несколько примеров употребления утилиты ls. После выполнения команд, показанных в листинге 4.4, наша Linux -система выдает информацию определенного вида (см. листинг 4.5):

    cd /usr/share/zoneinfo/posix
    ls -il UTC Universal Zulu
    2506763 -rw-r--r--    6 root     root           56 Apr 15  2002 UTC
    2506763 -rw-r--r--    6 root     root           56 Apr 15  2002 Universal
    2506763 -rw-r--r--    6 root     root           56 Apr 15  2002 Zulu

    Как и предписывает опция -i, выданы порядковые номера ( уникальные идентификаторы ) файлов. Мы видим, что для всех трех имен они одинаковы (2506763), т. е. эти имена ссылаются на один и тот же файл. Поскольку выданное утилитой ls число ссылок равно шести, в файловой системе имеются еще три (жесткие) ссылки на данный файл.

    Полезно сопоставить команды и результаты, показанные в листингах 4.4 и 4.5, с теми, что приведены в листингах 4.6 и 4.7.

    cd /bin
    ls -il ex rvi rview vi view
    4849722 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 ex -> vi
    4849723 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 rvi -> vi
    4849724 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 rview -> vi
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 vi
    4849726 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 view -> vi

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

    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 ex
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 rvi
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 rview
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 vi
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 view

    Данные о специальных файлах (см. листинг 4.9) могут выглядеть так, как показано в листинге 4.10.

    ls -l /dev/ttyp[0-3] /dev/fd[2-5]
    brw-rw----    1 root     floppy     2,   2 Apr  1  2002 /dev/fd2
    brw-rw----    1 root     floppy     2,   3 Apr  1  2002 /dev/fd3
    brw-rw----    1 root     floppy     2, 128 Apr  1  2002 /dev/fd4
    brw-rw----    1 root     floppy     2, 129 Apr  1  2002 /dev/fd5
    crw-rw-rw-    1 root     root       3,   0 Sep  2 17:21 /dev/ttyp0
    crw-rw-rw-    1 root     tty        3,   1 Apr  1  2002 /dev/ttyp1
    crw-rw-rw-    1 root     tty        3,   2 Apr  1  2002 /dev/ttyp2
    crw-rw-rw-    1 root     tty        3,   3 Apr  1  2002 /dev/ttyp3

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

    Сведения о файлах- каналах могут выглядеть так, как показано в листинге 4.11.

    total 0
    prw--w----    1 root     root            0 Sep  2 10:42 xdmctl
    prw--w----    1 shmyrev  root            0 Sep  2 10:42 xdmctl-:0

    В листинге 4.12 приведен пример вывода информации о сокетах.

    srwx------    1 shmyrev  root            0 Sep  2 10:42 /dev/gpmctl
    srw-rw-rw-    1 root     root            0 Sep  2 10:42 /dev/log

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

    ls -tl /

    может выдать следующую информацию (см. листинг 4.13):

    total 338
    drwxrwxrwt   23 root     root         8192 Sep  2 16:26 tmp
    dr-xr-xr-x  143 root     root            0 Sep  2 14:42 proc
    drwxr-xr-x   60 root     root         4096 Sep  2 10:42 etc
    drwxr-xr-x   18 root     root        86016 Sep  2 10:42 dev
    drwxr-x---    5 root     root         4096 Sep  1 17:10 root
    drwxr-xr-x    8 root     root         4096 Aug  8 13:16 lib
    drwxr-xr-x    7 root     root         4096 Jul 23 13:09 opt
    drwxr-xr-x   20 root     root         4096 Jul 15 18:00 home
    drwxr-xr-x   14 root     root         4096 Jul 15 12:50 usr
    drwxr-xr-x    2 root     root         4096 Jul 11 17:38 sbin
    drwxr-xr-x   16 root     root         4096 Jul  9 16:22 var
    drwxr-xr-x    2 root     root         4096 Jul  8 13:32 bin
    drwxr-xr-x    4 root     root         1024 Jul  6 15:06 boot
    drwx------    2 root     root        16384 Jul  6 14:15 lost+found
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 cdrom
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 a
    drwxr-xr-x    5 root     root         4096 Jul  6 11:24 mnt
    drwxr-xr-x    6 root     root         4096 Feb 23  2003 dss
    drwxr-xr-x    2 root     root         4096 Apr  1  2002 misc
    drwxr-xr-x    2 root     root         4096 Jun 12  2001 initrd

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

    При программировании на языке C информацию о файлах получают с помощью функций семейства stat() (см. листинг 4.14).

    #include <sys/stat.h>
    int stat (const char *restrict path, 
              struct stat *restrict buf);
    #include <sys/stat.h>
    int fstat (int fildes, struct stat *buf);
    #include <sys/stat.h>
    int lstat (const char *restrict path, 
               struct stat *restrict buf);

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

    Функция stat() предоставляет информацию о поименованном файле: аргумент path указывает на маршрутное имя файла. Чтобы получить эти сведения, достаточно иметь право на поиск для всех компонентов маршрутного префикса. Функция fstat() сообщает данные об открытом файле, задаваемом дескриптором файла   fildes. Функция lstat() эквивалентна stat() за одним исключением: если аргумент path задает символьную ссылку, lstat() возвращает информацию о ней, а stat() - о файле, на который эта ссылка указывает.

    В случае нормального завершения результат функций семейства stat() равен 0.

    Аргумент buf является указателем на структуру типа stat, в которую помещается информация о файле. Согласно стандарту POSIX-2001, в ней содержатся по крайней мере следующие поля:

    dev_t    st_dev;   
    /* Идентификатор устройства, содержащего файл */
    ino_t    st_ino;   
    /* Порядковый номер файла в файловой системе */
    mode_t   st_mode;  
    /* Режим файла 
    nlink_t  st_nlink; 
    /* Число жестких ссылок на файл */
    uid_t    st_uid;   
    /* Идентификатор владельца файла */
    gid_t    st_gid;   
    /* Идентификатор владеющей группы */
    off_t    st_size;  
    /* Для обычных файлов и символьных ссылок - 
                          размер в байтах */
    /* Для файлов других типов значение этого поля 
                          неспецифицировано */
    time_t   st_atime; 
    /* Время последнего доступа */
    time_t   st_mtime; 
    /* Время последнего изменения файла */
    time_t   st_ctime; 
    /* Время последнего изменения статуса файла */

    Некоторые пояснения. Комбинация значений ( st_dev, st_ino ) должна однозначно определять файл в пределах объединенной (в том числе сетевой) файловой системы. Статус файла меняется, когда модифицируются его атрибуты (например, режим ), а не содержимое.

    В файле   <sys/stat.h> определена не только структура stat, но и константы, полезные для работы с битами режима. Так, S_IFMT выделяет тип файла, S_IFREG обозначает обычные файлы, S_IRWXU - биты режима доступа владельца и т.д.

    Приведем пример использования функций stat() и lstat() (см. листинг 4.15).

    /* Программа выдает информацию о файлах - аргументах командной строки */
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    
    /* Функция возвращает односимвольное обозначение типа файла */
    /* Для неизвестного типа возвращается 'u'                   */
    static char my_filetype (const mode_t mode) {
     switch (mode S_IFMT) {
     case S_IFDIR:
       return ('d');
     case S_IFBLK:
       return ('b');
     case S_IFCHR:
       return ('c');
     case S_IFLNK:
       return ('l');
     case S_IFIFO:
       return ('p');
     case S_IFREG:
       return ('-');
     case S_IFSOCK:
       return ('s');
     default:
       return ('u');
     }
    } 
    int main (int argc, char *argv[]) {
     struct stat buf;
     int i;
     for (i = 1; i < argc; i++) {
       if (stat (argv [i], buf)) {
         fprintf (stderr, "\nstat: не удалось получить информацию о файле %s\n", argv [i]);
         return (-1);
       }
    
       printf ("\nstat-информация о файле %s:\n", argv [i]);
       printf ("Тип: %c\n", my_filetype (buf.st_mode));
       printf ("Размер: %ld\n", buf.st_size);
     
       if (lstat (argv [i], buf)) {
         fprintf (stderr, "\nlstat: не удалось получить информацию о файле %s\n", argv [i]);
         return (-1);
       }
    
       printf ("\nlstat-информация о файле %s:\n", argv [i]);
       printf ("Тип: %c\n", my_filetype (buf.st_mode));
       printf ("Размер: %ld\n", buf.st_size);
     }
    
     return 0;
    }

    Возможный результат работы этой программы с аргументами /bin/view и /bin/vi показан в листинге 4.16.

    stat-информация о файле /bin/view:
    Тип: -
    Размер: 386120
    lstat-информация о файле /bin/view:
    Тип: l
    Размер: 2
    stat-информация о файле /bin/vi:
    Тип: -
    Размер: 386120
    lstat-информация о файле /bin/vi:
    Тип: -
    Размер: 386120

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

    df  [опция ...] [файл ...]

    или функциями fstatvfs() или statvfs() (см. листинг 4.17).

    #include <sys/statvfs.h>
    int fstatvfs (int fildes, 
                  struct statvfs *buf);
    int statvfs (const char *restrict path, 
                 struct statvfs *restrict buf);

    Утилита df выдает на стандартный вывод информацию об имеющемся пространстве и, возможно, некоторые другие данные. Если файлы-аргументы не указаны, поступают сообщения обо всех компонентах объединенной файловой системы.

    По умолчанию пространство измеряется в 512-байтных блоках; опция -k предписывает использовать блоки размером в килобайт. Подразумеваемый формат вывода не специфицирован, но должны становиться известными по крайней мере имя файловой системы и объем свободного пространства. Если задана опция -P, употребляется так называемый мобильный формат вывода с заголовком

    Filesystem  512-blocks  Used Available 
                           Capacity Mounted on

    и следующей построчной информацией о каждой файловой системе:

  • имя файловой системы ;
  • общее пространство ;
  • занятое пространство ;
  • свободное пространство ;
  • процент занятости ;
  • корень файловой системы ( точка монтирования в объединенной файловой системе ).
  • Возможный результат выполнения служебной программы df с опциями -kP приведен в листинге 4.18.

    Filesystem         1024-blocks      Used Available Capacity Mounted on
    /dev/sda3             75822432  43456504  28514348      61% /
    /dev/sda1                46636     21129     23099      48% /boot
    none                    257212         0    257212       0% /dev/shm
    /dev/fd0                  1424       392      1032      28% /a

    Пара функций statvfs() и fstatvfs() по интерфейсу аналогична функциям stat() и fstat(), только в выходную структуру (типа fstatvfs ) помещается информация не о файлах, а о файловых системах, содержащих эти файлы. Согласно стандарту POSIX-2001, в структуре statvfs должны присутствовать по крайней мере следующие поля:

    unsigned long f_bsize;    
    /* Размер блока файловой системы */
    unsigned long f_frsize;   
    /* Базовый размер блока файловой системы */
    fsblkcnt_t    f_blocks;   
    /* Общее число блоков базового размера в 
    файловой системе */
    fsblkcnt_t    f_bfree;    
    /* Общее число свободных блоков */
    fsblkcnt_t    f_bavail;   
    /* Число свободных блоков, доступных 
    непривилегированным процессам */
    fsfilcnt_t    f_files;    
    /* Общее число описателей файлов */
    fsfilcnt_t    f_ffree;    
    /* Общее число свободных описателей файлов */
    fsfilcnt_t    f_favail;   
    /* Число описателей файлов, доступных 
    непривилегированным процессам */
    unsigned long f_fsid;     
    /* Идентификатор файловой системы */
    unsigned long f_flag;     
    /* Битная шкала флагов */
    unsigned long f_namemax;  
    /* Максимальная длина имени файла */

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

    В листинге 4.19 приведен исходный текст программы, которая выдает сведения о файловых системах, содержащих заданные в командной строке файлы-аргументы.

    #include <sys/statvfs.h>
    #include <stdio.h>
    
    int main (int argc, char *argv[]) {
     struct statvfs buf;
     int i;
    
     for (i = 1; i < argc; i++) { 
       if (statvfs (argv [i], buf)) {
         fprintf (stderr, "\nstatvfs: не удалось получить информацию о файловой системе, содержащей файл %s\n", argv [i]);
         return (-1);
       }
    
       printf ("\nstatvfs-информация о файловой системе, содержащей файл %s:\n", argv [i]);
       printf ("Размер блока файловой системы: %ld\n", buf.f_bsize);
       printf ("Базовый размер блока файловой системы: %ld\n", buf.f_frsize);
       printf ("Общее число блоков базового размера в файловой системе: %ld\n", buf.f_blocks);
       printf ("Общее число свободных блоков: %ld\n", buf.f_bfree);
       printf ("Число свободных блоков, доступных непривилегированным процессам: %ld\n", buf.f_bavail);
       printf ("Общее число описателей файлов: %ld\n", buf.f_files);
       printf ("Общее число свободных описателей файлов: %ld\n", buf.f_ffree);
       printf ("Число описателей файлов, доступных непривилегированным процессам: %ld\n", buf.f_favail);
       printf ("Идентификатор файловой системы: %ld\n", buf.f_fsid);
       printf ("Битная шкала флагов: %lx\n", buf.f_flag);
       printf ("Максимальная длина имени файла: %ld\n", buf.f_namemax);
     }
    
     return 0;
    }

    Если смонтировать   файловую систему FAT на каталог   /a с опцией "только на чтение ", то результат выполнения приведенной программы с аргументами / и /a может выглядеть так, как показано в листинге 4.20.

    statvfs-информация о файловой системе, содержащей файл /:
    Размер блока файловой системы: 4096
    Базовый размер блока файловой системы: 4096
    Общее число блоков базового размера в файловой системе: 18955608
    Общее число свободных блоков: 7990010
    Число свободных блоков, доступных непривилегированным процессам: 7027115
    Общее число описателей файлов: 9633792
    Общее число свободных описателей файлов: 8259049
    Число описателей файлов, доступных непривилегированным процессам: 8259049
    Идентификатор файловой системы: 0
    Битная шкала флагов: 0
    Максимальная длина имени файла: 255
    
    statvfs-информация о файловой системе, содержащей файл /a:
    Размер блока файловой системы: 512
    Базовый размер блока файловой системы: 512
    Общее число блоков базового размера в файловой системе: 2847
    Общее число свободных блоков: 1960
    Число свободных блоков, доступных непривилегированным процессам: 1960
    Общее число описателей файлов: 0
    Общее число свободных описателей файлов: 0
    Число описателей файлов, доступных непривилегированным процессам: 0
    Идентификатор файловой системы: 0
    Битная шкала флагов: f
    Максимальная длина имени файла: 260

    Интерпретация полученных результатов предоставляется читателю.

    Служебная программа

    du  [опция ...] [файл ...]

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

    Выделим несколько других опций.

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

    Пример. В ответ на команду

    du -k /usr/local/man /bin/vi /bin/view

    может быть выдано следующее (см. листинг 4.21):

    1428    /usr/local/man/man1
    12  /usr/local/man/man5
    64  /usr/local/man/man7
    36  /usr/local/man/man8
    1544    /usr/local/man
    384 /bin/vi
    0   /bin/view

    Таким образом, иерархия файлов с корнем в /usr/local/man занимает около 1.5 Мб (почти все приходится на долю файлов подкаталога man1 ), обычный файл   /bin/vi - 384 Кб (читателю предлагается сопоставить эту величину и приведенный выше размер данного файла в байтах), символьная ссылка лишнего места не занимает.

    Изменение атрибутов файлов и текущей позиции в файловой иерархии

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

    cd  [-L | -P] [целевой_каталог]
    cd  -

    и функция chdir():

    #include <unistd.h>
    int chdir (const char *path);

    Команда cd без аргументов осуществляет переход в домашний каталог пользователя, заданный переменной окружения HOME. Если аргументом является минус, выполняются действия, показанные в листинге 4.22: осуществляется переход в каталог, ранее бывший текущим, и в случае успеха выводится его абсолютное маршрутное имя.

    cd "$OLDPWD"  pwd

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

  • если имя начинается с точки или точки-точки, перед ним подставляется значение переменной окружения $PWD и / ;
  • в других случаях вместо $PWD подставляются элементы списка, являющегося значением переменной окружения $CDPATH и устроенного аналогично $PATH ; процесс продолжается до тех пор, пока не получится существующий каталог ; в случае необходимости в последнюю очередь используется значение $PWD.
  • Затем выполняется раскрытие символьных ссылок и устранение имен "точка" и "точка-точка" ("точка" уничтожается вместе со следующим за ней символом /, "точка-точка" - вместе с предыдущим компонентом, отличным от "точки-точки", и символом / между ними). Опции команды cd влияют на порядок выполняемых действий. По опции -P сначала раскрываются символьные ссылки. Это значит, что "точка-точка" трактуется как физический надкаталог ( каталог, вышележащий по отношению к указуемому файлу ). При наличии (подразумеваемой) опции -L порядок действий обратный; в результате "точка-точка" обозначает логический надкаталог ( каталог, вышележащий по отношению к символьной ссылке, а не к указуемому файлу ).

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

    В случае успешной смены текущего каталога соответственно изменяются значения переменных окружения $OLDPWD ( текущий каталог непосредственно перед выполнением команды cd ) и $PWD ( текущий каталог после выполнения команды cd ).

    Рассмотрим пример выполнения команды cd с разными опциями (см. листинг 4.23).

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

    ls -dl /usr/tmp /var/tmp
    cd /usr/tmp
    pwd
    pwd -P
    cd ..
    pwd
    cd -
    cd -P ..
    pwd
    lrwxrwxrwx    1 root     root           10 Jul  6 14:16 /usr/tmp -> ../var/tmp
    drwxrwxrwt    5 root     root         4096 Sep  4 18:05 /var/tmp
    /usr/tmp
    /var/tmp
    /usr
    /usr/tmp
    /var

    Можно видеть, что /usr/tmp является символьной ссылкой на каталог   /var/tmp. При варьировании опций команды pwd   каталог   /usr/tmp по-разному отображается в качестве текущего (напомним, опция -P команды pwd вызывает раскрытие символьных ссылок ). По-разному срабатывает и команда cd с целевым каталогом "точка-точка", опцией -P и без таковой. В результате видно различие между физическим и логическим надкаталогами символьной ссылки.

    Для изменения атрибутов файлов служат утилиты

    chown  [-R] [-H | -L | -P ] владелец[:группа] 
           файл ...

    (смена владельца и, быть может, владеющей группы файла) и

    chmod  [-R] изменение_режима файл ...

    (модификация режима файла ), а также аналогичные им по назначению и именам функции (см. листинг 4.25).

    #include <unistd.h>
    int chown (const char *path, uid_t owner, 
               gid_t group);
    #include <unistd.h>
    int fchown (int fildes, uid_t owner, 
                gid_t group);
    #include <sys/stat.h>
    int chmod (const char *path, mode_t mode);
    #include <sys/stat.h>
    int fchmod (int fildes, mode_t mode);

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

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

    Опции -R, -H и -L имеют в целом тот же смысл, что и для утилиты ls (см. выше): первая предписывает рекурсивный обход встретившихся подкаталогов, две другие - выборочное или полное раскрытие символьных ссылок, указывающих на каталоги. Опция -P означает, что изменения относятся к самим символьным ссылкам.

    Если при использовании функций chown() и fchown() меняется лишь владелец, то аргумент group задается равным ( gid_t ) (-1); при смене только группы идентификатор владельца следует задать как ( uid_t ) (-1).

    Задавая аргумент изменение_режима служебной программы chmod   владельца файла обозначают буквой u, владеющую группу - буквой g, прочих пользователей - o. Добавлению прав соответствует знак +, их удаление помечается знаком -. Знак = обозначает буквальное задание прав (для указанной категории пользователей устанавливается указанный режим доступа ). После букв, определяющих категорию пользователей, и знака операции следуют сами добавляемые (удаляемые, устанавливаемые) режимы доступа - обычно r, w и/или x ; можно указать несколько подобных связок, разделяя их запятыми (без пробелов).

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

    chmod go-w,u+x myfile

    Как указывалось выше, для удобства программирования аналогичных действий на языке C в заголовочном файле   <sys/stat.h> определены константы, соответствующие битам режима файлов. Например, S_IRUSR соответствует праву на чтение для владельца, S_IRWXG охватывает все три бита режима доступа для владеющей группы и т.п. С помощью побитных операций с текущим режимом файла и этими константами можно сформировать требуемое значение аргумента mode функции chmod() (см. листинг 4.27).

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    /* Программа добавляет права на выполнение для файлов - аргументов командной строки */
    int main (int argc, char *argv[]) {
     struct stat buf;
     int i;
    
     for (i = 1; i < argc; i++) {
       if (stat (argv [i], buf)) {
         fprintf (stderr, "\nstat: не удалось получить информацию о файле %s\n", argv [i]);
         return (-1);
       }
     
       if (chmod (argv [i], buf.st_mode | S_IXUSR | S_IXGRP | S_IXOTH)) {
         fprintf (stderr, "\nchmod: не удалось изменить режим доступа к файлу %s\n", argv [i]);
         return (-1);
       }
     }
    
     return 0;
    }

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

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

    touch  [-am] [-r эталонный_файл | -t время]
           [-c] файл ...

    Опция -a предписывает модифицировать время последнего доступа, опция -m - время последнего изменения; если не задана ни одна из них, выполняется и то, и другое.

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

    [[CC]YY]MMDDhhmm[.SS]

    Пары цифр указывают, соответственно, на две старшие цифры года, две младшие цифры года, месяц в году, номер дня в месяце, час в дне, минуту в часе и секунду в минуте.

    (Любопытны правила формирования старших цифр года, если они опущены. Когда младшие цифры лежат в диапазоне от 69 до 99, подразумевается 19; в противном случае - 20. Несомненно, в будущих версиях стандарта данное соглашение изменится.)

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

    Приведем пример совместного использования служебных программ touch, chown и chmod. В процессе загрузки ОС Linux выполняются действия по инициализации файлов с информацией о пользовательских сеансах, подобные тем, что приведены в листинге 4.28.

    > /var/run/utmp
    touch /var/log/wtmp
    chown :utmp /var/run/utmp /var/log/wtmp
    chmod ug=rw,o=r /var/run/utmp /var/log/wtmp

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

    Часто touch применяют при работе с файлами-замками (см. листинг 4.29).

    start () {
     echo -n "Starting cupsd: "
     daemon cupsd
     RETVAL=$?
     echo
     [ $RETVAL = 0 ]  touch /var/lock/subsys/cups
     return $RETVAL
    }

    Создание, удаление, копирование и перемещение файлов

    Стандарт POSIX-2001 не требует наличия утилиты для создания   обычных файлов. Они появляются по мере необходимости как побочный продукт многочисленных служебных программ (например, утилиты копирования), поэтому сама постановка задачи - создать файл "просто так" - является отчасти надуманной. В то же время, если новый файл все-таки нужен, полезно иметь в виду возможность перенаправления вывода пустой команды, которая имеется в языке shell.

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

    #include <fcntl.h>
    int creat (const char *path, mode_t mode);

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

    Если файл, который пытаются создать при помощи creat(), уже существует, он опустошается (размер становится равным 0), а режим доступа и владелец не изменяются.

    В случае неудачи результат creat() равен -1, а внешней переменной errno присваивается код ошибки, позволяющий определить причину ее (ошибки) возникновения. Переменная errno, а также мнемоники для кодов ошибок определены в заголовочном файле   <errno.h>. Для формирования системного сообщения об ошибке можно воспользоваться функцией perror() (см. листинг 4.31), которая, опираясь на значение errno, помещает в стандартный протокол описание последней ошибки.

    #include <stdio.h>
    void perror (const char *s);

    Например, при первом выполнении программы, приведенной в листинге 4.32, в стандартный протокол может быть выдан соответствующий результат (см. листинг 4.33).

    #include <fcntl.h>
    #include <errno.h>
    #include <stdio.h>
    #include <limits.h>
    /* Программа пытается создавать в текущем  */
    /* каталоге файлы с именами g1, g2, ...,   */
    /* пока эти попытки не закончатся неудачей */
    int main (void) {
     int  n = 0;
     char name [PATH_MAX]; 
     do
       sprintf (name, "g%d", ++n);
     while (creat (name, (mode_t) 0) >= 0);
     perror ("CREAT failed");
     fprintf (stderr, "errno = %d\n", errno);
     fprintf (stderr, 
             "Неудача на файле номер %d\n", n);
     return 0;
    }
    CREAT failed: Too many open files
    errno = 24
    Неудача на файле номер 1022

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

    CREAT failed: Permission denied
    errno = 22
    Неудача на файле номер 1

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

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

    Рассмотренный в примере стиль уведомления о неудачном завершении общий для большинства функций. Неудача определяется возвращением результата, невозможного в другом случае (почти всегда это -1 или пустой указатель NULL ); код ошибки заносится в переменную errno. Разумеется, в реальных программах errno не выводят, а анализируют. В данном случае уместно было бы сравнивать errno с константами EACCES, EINVAL, EMFILE и т.д. (см. <errno.h> ).

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

    df = creat ("/tmp/sample", 
                S_IRWXU | S_IRWXG | S_IRWXO);

    Для создания (пустых) каталогов служит утилита

    mkdir  [-p] [-m режим_доступа]  каталог ...

    и функция mkdir()

    #include <sys/stat.h>
    int mkdir (const char *path, mode_t mode);

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

    Если нужен не один, а целая цепочка каталогов, следует воспользоваться опцией -p. Соответствующий пример приведен в листинге 4.36.

    mkdir -p work/tmp/save

    По этой команде в текущем каталоге появится подкаталог work, в нем - каталог   tmp, а уже в нем - каталог   save.

    Смысл аргументов функции mkdir() тот же, что и для creat(), в случае неудачи возвращается -1, но при нормальном завершении результат равен 0 ( mkdir() не оставляет новый каталог открытым). Перечисленные выше условия неудачного завершения creat() (кроме превышения максимального числа одновременно открытых файлов ) применимы и к mkdir().

    Интерфейс, сходный с mkdir, имеют утилита mkfifo для создания каналов:

    mkfifo  [-m режим_доступа] канал ...

    Заметим, что здесь отсутствует опция -p ) и одноименная функция (см. листинг 4.37).

    #include <sys/stat.h>
    int mkfifo (const char *path, mode_t mode);

    Создать новые ссылки ( жесткие или символьные) на файл позволяют служебная программа ln:

    ln  [-fs] исходный_файл целевой_файл
    ln  [-fs] исходный_файл ... целевой_каталог

    а также функции link() и symlink() (см. листинг 4.38).

    #include <unistd.h>
    int link (const char *source_path, 
              const char *target_path);
    #include <unistd.h>
    int symlink (const char *link_contents, 
                 const char *link_name);

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

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

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

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

    Опция -f позволяет замещать существующие элементы каталогов новыми (по умолчанию задание существующего файла в качестве целевого считается ошибкой).

    Примером смены содержимого символьной ссылки посредством служебной программы ln с опциями -s и -f может служить фрагмент действий при загрузке ОС Linux, приведенный в листинге 4.39.

    if [ -n "$mver" ]; then
     ln -sf /lib/modules/$mver\
            /lib/modules/default
    fi

    Для удаления файлов служат утилиты

    rm  [-fiRr] файл ...

    и

    rmdir  [-p] файл ...

    а также функции unlink(), rmdir() и remove() (см. листинг 4.40).

    #include <unistd.h>
    int unlink (const char *path);
    #include <unistd.h>
    int rmdir (const char *path);
    #include <stdio.h>
    int remove (const char *path);

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

    Опишем опции утилиты rm. Опция -i означает удаление файлов с предварительным запросом подтверждения.

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

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

    В общем случае подтверждение на удаление запрашивается при выполнении следующего условия:

  • (не задана опция -f) И
  • (((отсутствует право на запись в файл) И
  • (стандартный ввод назначен на терминал)) ИЛИ
  • (задана опция -i)))
  • Наконец, две эквивалентные "сверхмощные" опции -R и -r (первая предусмотрена стандартом POSIX, вторая поддерживается по историческим причинам) позволяют удалять каталоги со всем их содержимым (если, конечно, хватает прав). Так, в процессе загрузки некоторых Unix -систем выполняется команда, которая удаляет каталог   /tmp и все содержащиеся в нем файлы и подкаталоги:

    rm -rf /tmp

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

    Для удаления каталогов (правда, только пустых) рекомендуется использовать служебную программу rmdir (или одноименную функцию). Опция -p предписывает удалять всю цепочку каталогов, составляющих заданное маршрутное имя. Например, если в текущем каталоге есть подкаталог a, содержащий только пустой подкаталог b, то команда, показанная в листинге 4.41, удалит и b, и a.

    rm -p a/b

    Функция unlink() предназначена для удаления файлов, не являющихся каталогами, а функция remove(), имеющая в стандарте C99 [5], объединяет возможности функций rmdir() и unlink().

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

    #include <unistd.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <sys/stat.h>
    /* Программа выполняет обработку */
    /* с осторожным замещением       */
    /* основного файла рабочим       */
    #define MAIN_FILE "/home/galat/garb/temp/mfile"
    #define OLD_FILE  "/home/galat/garb/temp/ofile"
    #define WORK_FILE "/home/galat/garb/temp/wfile"
    int main (void) {
     /* Необходимые описания */
     /* . . .                */
     int work_success = 1;
    
     /* Выполнение операций над рабочим файлом */
     /* . . .                                  */
    
     /* В случае неудачи выдадим диагностическое сообщение и удалим рабочий файл */
     if (! work_success) {
       fprintf (stderr, "\nНеудачное завершение операций над рабочим файлом %s\n", WORK_FILE);
       unlink (WORK_FILE);
       return (-1);
     }
    
     /* Установим режим доступа к рабочему файлу,  */
     /* подходящие для последующего использования  */
     /* Пусть, например, это будет доступ для всех */
     /* только на чтение                           */
     chmod (WORK_FILE, S_IRUSR | S_IRGRP | S_IROTH);
    
     /* Удалим ранее сохраненную старую версию основного файла */
     unlink (OLD_FILE);
    
     /* Сохраним текущую версию основного файла */
     if (link (MAIN_FILE, OLD_FILE)) {
       perror ("Не удалось сохранить текущую версию основного файла");
       return (-1);
     }
    
     /* Удалим текущую версию основного файла */
     unlink (MAIN_FILE);
    
     /* Сделаем рабочий файл основным */
     if (link (WORK_FILE, MAIN_FILE)) {
       perror ("Не удалось сделать рабочий файл основным");
       /* Восстановим основной файл */
       link (OLD_FILE, MAIN_FILE);
       return (-1);
     }
    
     /* Удалим рабочий файл */
     unlink (WORK_FILE);
    
     return 0;
    }

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

    cp  [-fip] исходный_файл целевой_файл
    cp  [-fip] исходный_файл ... целевой_каталог
    cp  -R [-H | -L | -P] [-fip] исходный_файл 
        ... целевой_каталог

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

    При наличии опции -R выполняется рекурсивное копирование исходных файловых иерархий в целевой каталог со стандартной трактовкой символьных ссылок, управляемой опциями -H, -L и -P (см. выше, например, описание утилиты chown ). Если копирование производится в существующий каталог, имена копий образуются как конкатенация целевого каталога, символа / и маршрута относительно каталога, содержащего исходный_файл. Если в командной строке заданы два файла и целевой не существует, он создается и становится корнем иерархии-копии, то есть исходный_файл копируется непосредственно в него, а маршруты файлов нижележащих уровней считаются относительно исходного_файла.

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

    Опция -i влечет запрос подтверждения перед попыткой копирования в существующий файл.

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

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

    Синтаксис, с точностью до опций аналогичный двум первым формам cp, имеет вызов служебной программы перемещения файлов   mv:

    mv  [-fi] исходный_файл целевой_файл mv  
        [-fi] исходный_файл ... 
    	существующий_целевой каталог

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

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

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

    Приведем пример употребления утилиты mv. Пусть текущий каталог содержит только файлы   x и y и пустой каталог   d1 (см. листинг 4.43). Тогда после выполнения команд, показанных в листинге 4.44, будет создан каталог   d2, где окажутся файлы   x и y, а каталог   d1 исчезнет (см. листинг 4.45).

    .: d1/  x  y
    ./d1:
    ls -RF
    mv x y d1
    mv d1 d2
    ls -RF
    .:
    d2/
    ./d2:
    x  y

    Продолжим этот пример двумя одинаковыми командами копирования (см. листинг 4.46).

    cp -R d2 d1
    cp -R d2 d1
    ls -RF

    Первая команда скопирует иерархию с корнем d2 во вновь созданный каталог   d1, вторая - под d1 (с сохранением имени d2 для корня копии). Результат показан в листинге 4.47.

    .:
    d1/  d2/
    ./d1:
    d2/  x  y
    ./d1/d2:
    x  y
    ./d2:
    x  y

    Для перемещения (переименования) одного файла служит функция rename() из репертуара C99 (см. листинг 4.48).

    #include <stdio.h>
    int rename (const char *old_path, 
                const char *new_path);

    Функция rename() обладает частью функциональности служебной программы mv. Она не перемещает файловых иерархий и не раскрывает символьных ссылок, если их имена заданы в качестве аргументов. С другой стороны, rename() поддерживает переименование каталогов. В этом случае аргумент new_path не должен задавать файл другого типа, а если он задает существующий каталог, тот, по понятным причинам, должен быть пустым.

    В продолжение двух предыдущих примеров продемонстрируем переименование каталогов (см. листинг 4.49). Итоговое состояние текущего каталога показано в листинге 4.50.

    #include <stdlib.h>
    #include <stdio.h>
    #define OLD_DIR "d1/d2"
    #define NEW_DIR "d2"
    int main (void) {
     system ("rm -f " NEW_DIR "/*");
     if (rename (OLD_DIR, NEW_DIR)) {
       perror ("RENAME failed");
       return (-1);
     }
     system ("ls -RF");
     return 0;
    }
    .:
    d1/  d2/
    ./d1:
    x  y
    ./d2:
    x  y

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

    Обход и обработка файловых иерархий

    Для обхода файловой иерархии и систематической обработки ее элементов служит утилита find:

    find  [-H | -L] файл ... [выражение]

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

    Перечислим элементарные логические выражения и правила их вычисления. (Далее n обозначает целое десятичное число; вместо него могут указываться также комбинации +n и -n, что означает, соответственно, "больше, чем n" и "меньше, чем n".)

    -name шаблон_файлов

    Истина, если имя текущего файла удовлетворяет шаблону_файлов. Символы шаблона, имеющие для shell специальный смысл, должны быть экранированы.

    -type тип_файла

    Истина, если файл имеет заданный тип: b, c, d, f, p или s - является, соответственно, блочным или символьным специальным файлом, каталогом, обычным файлом, каналом или сокетом.

    -size n[c]

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

    -atime n

    Истина, если последний доступ к файлу производился n дней назад (в данном контексте день - это промежуток времени в 86400 секунд).

    -mtime n

    Истина, если файл последний раз модифицировался n дней назад.

    -ctime n

    Истина, если атрибуты файла последний раз изменялись n дней назад.

    -perm [-]режим

    Истина, если режим файла соответствует заданному. При наличии знака минус соответствие понимается как включение (все заданные биты должны присутствовать в режиме доступа к файлу ); если минус отсутствует, требуется точное совпадение. Режим задается аналогично утилите chmod.

    -links n

    Истина, если на файл имеется n жестких ссылок.

    -user имя_пользователя

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

    -nouser

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

    -group владеющая_группа

    Истина, если файлом владеет заданная группа.

    -nogroup

    Истина, если идентификатор владеющей группы файла отсутствует в базе данных групп.

    -depth

    Всегда истина; задает дисциплину обхода иерархии вглубь: сначала обрабатываются все элементы каталога, потом - сам каталог (по умолчанию find в первую очередь обрабатывает каталог и лишь после этого - его элементы ).

    -xdev

    Всегда истина; предписывает не спускаться в каталоги, имеющие другой идентификатор устройства ( st_dev, см. выше описание структуры stat ).

    -prune

    Всегда истина; предписывает не обрабатывать текущий файл, если он является каталогом.

    -exec команда

    Истина, если после выполнения команды возвращается нулевой код завершения. запись команды должна заканчиваться экранированной точкой с запятой. Аргумент команды, заданный в виде пары фигурных скобок {}, заменяется маршрутным именем обрабатываемого файла.

    -ok команда

    Эквивалентно -exec за исключением того, что перед выполнением команды запрашивается подтверждение (в виде сгенерированной командной строки со знаком вопроса в конце), и она выполняется только при ответе y.

    -print

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

    -newer файл

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

    ( выражение )

    Истина, если истинно заключенное в скобки выражение (скобки должны быть экранированы от интерпретации языком shell).

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

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

    find . -name \*.c -print | wc -l

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

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

    cat `find . -name \*.c -print` | wc -l

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

    find . -name \*.c -exec cat {} \; | wc -l

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

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

    wc -l `find . -name \*.c -print`

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

    find . \( -size 0c -o -name \*.o -a -atime 
             +30 \) -ok rm {} \;

    Пусть на каталог   /mnt   смонтирован съемный носитель. Требуется выявить на нем файлы, владелец или группа которых на данной системе неизвестны, и изменить их на nobody. К цели ведет команда, показанная в листинге 4.52 (предполагается, что тот, кто ее выполняет, обладает соответствующими привилегиями).

    find /mnt \( -nouser -o -nogroup \) -exec 
         chown nobody:nobody {} \;

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

    Две команды, показанные в листинге 4.53, почти эквивалентны: обе не заходят в подкаталоги с именем skip   текущего каталога, но только вторая выводит их имена.

    find . -name skip -prune -o -print find 
         . -print -name skip -prune

    Одной из форм обхода и обработки файловой иерархии можно считать архивирование. Стандарт POSIX предусматривает для этого служебную программу pax:

    pax  [опция ...] [шаблон ...]
    pax  -r [опция ...] [шаблон ...]
    pax  -w [опция ...] [файл ...]
    pax  -r -w [опция ...] [файл ...] каталог

    Утилита pax поддерживает несколько архивных форматов, в число которых входят "родной" pax и унаследованные cpio и tar. В зависимости от заданной комбинации опций -r и -w pax осуществляет одну из четырех операций: выводит оглавление архива (первая из приведенных выше форм команды pax ), читает архив и извлекает из него файлы по заданным шаблонам имен (вторая форма), записывает заданные файлы в архив (третья форма) или копирует иерархии файлов (четвертая форма).

    Как правило, архив читается со стандартного ввода, а записывается на стандартный вывод; если встречается файл   типа " каталог ", обработке подвергается вся иерархия файлов с корнем в этом каталоге.

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

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

    В листинге 4.54 представлены примеры использования команды pax. Первая архивирует содержимое текущего каталога, последняя копирует каталог   old_dir в new_dir.

    pax -w .
    mkdir new_dir
    pax -rw old_dir new_dir
    Страницы:

    Основные понятия.

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

    В стандарте зафиксированы следующие типы файлов:

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

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

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

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

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

    Сокет - это оконечная точка межпроцессных коммуникаций.

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

    Согласно стандарту POSIX-2001, для каждого процесса определен корневой каталог с именем / - вершина графа, из которой осуществляется доступ к прочим файлам данной файловой системы . "Абсолютного", единого для всех корня может и не существовать.

    В пределах файловой системы каждый файл имеет уникальный идентификатор ( порядковый номер - он же номер описателя файла ).

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

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

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

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

    Под маршрутным именем понимается цепочка символов, идентифицирующая файл и состоящая из нескольких (в том числе из нуля) простых имен файлов, разделенных символами / и называемых компонентами маршрута.   Маршрутные имена, начинающиеся символом /, называются абсолютными, они задают маршрут от корня файловой системы . (Отметим, что стандарт POSIX-2001 определяет трактовку маршрутных имен, начинающихся с двух символов /, как зависящую от реализации. В частности, таким способом в некоторых ОС задаются имена файлов на других хостах сети.)

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

    Маршрутным префиксом называется маршрутное имя, ссылающееся на каталог . Если маршрутное имя оканчивается символом /, оно обязано быть маршрутным префиксом.

    Маршрутные имена называют также составными   именами файлов . Примеры составных имен:

    /.autofsck
    /usr/lib/tcl
    ../susv3
    ./l4.txt

    Первые два имени являются абсолютными. файл   .autofsck лежит в корневом каталоге, файл   tcl - в подкаталоге lib подкаталога usr   корневого каталога. Третье и четвертое - относительные имена, специфицирующие, соответственно, файл   susv3, расположенный в иерархии на один уровень выше файлов   текущего каталога, и файл   l4.txt из текущего каталога. Последнее маршрутное имя можно было задать и проще:

    l4.txt

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

    Согласно стандарту, с каждым файлом ассоциирована по крайней мере следующая служебная информация:

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

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

    По отношению к конкретному файлу все пользователи делятся на три категории:

  • владелец файла ;
  • члены владеющей группы;
  • прочие пользователи.
  • Для каждой из них режим доступа определяет права на операции с файлом, а именно:

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

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

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

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

    Получение информации о файлах и файловых системах

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

    pwd  [-L | -P]

    #include <unistd.h>
    char *getcwd (char *buf, size_t size);

    Команда pwd с (подразумеваемой) опцией -L извлекает имя текущего каталога из переменной окружения PWD, если это возможно. Посредством опции -P выполняется раскрытие символьных ссылок: в выдаваемом на стандартный вывод абсолютном маршрутном имени вместо символьных ссылок подставляется их содержимое.

    Функция getcwd() помещает абсолютное маршрутное имя   текущего каталога (без символьных ссылок) в массив buf длины size, который и возвращается в качестве результата (при ошибке результат равен NULL).

    Приведем пример программы, использующей функцию getcwd() (см. листинг 4.2).

    #include <stdlib.h>
    #include <unistd.h>
    #include <stdio.h>
    int main (void) {
     size_t size;
     char *buf;
     char *apath;
     /* Выясним, каким должен быть размер буфера            */
     /* для абсолютного маршрутного имени текущего каталога */
     size = (size_t) pathconf (".", _PC_PATH_MAX);
     if ((buf = (char *) malloc (size)) == NULL) {
       fprintf (stderr, "\nНе удалось выделить буфер размера %d\n", size);
       return 1;
     }
      if ((apath = getcwd (buf, size)) == NULL) {
       fprintf (stderr, "\nНе удалось определить абсолютное маршрутное имя текущего каталога\n");
       return 1;
     }
     printf ("\nАбсолютное маршрутное имя текущего каталога: %s\n", apath);
     return 0;
    }

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

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

    ls  [опция ...] [файл ...]

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

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

    ls -l /

    может быть выдано следующее (см. листинг 4.3):

    total 338
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 a
    drwxr-xr-x    2 root     root         4096 Jul  8 13:32 bin
    drwxr-xr-x    4 root     root         1024 Jul  6 15:06 boot
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 cdrom
    drwxr-xr-x   18 root     root        86016 Sep  2 10:42 dev
    drwxr-xr-x    6 root     root         4096 Feb 23  2003 dss
    drwxr-xr-x   60 root     root         4096 Sep  2 10:42 etc
    drwxr-xr-x   20 root     root         4096 Jul 15 18:00 home
    drwxr-xr-x    2 root     root         4096 Jun 12  2001 initrd
    drwxr-xr-x    8 root     root         4096 Aug  8 13:16 lib
    drwx------    2 root     root        16384 Jul  6 14:15 lost+found
    drwxr-xr-x    2 root     root         4096 Apr  1  2002 misc
    drwxr-xr-x    5 root     root         4096 Jul  6 11:24 mnt
    drwxr-xr-x    7 root     root         4096 Jul 23 13:09 opt
    dr-xr-xr-x   84 root     root            0 Sep  2  2003 proc
    drwxr-x---    5 root     root         4096 Sep  1 17:10 root
    drwxr-xr-x    2 root     root         4096 Jul 11 17:38 sbin
    drwxrwxrwt   23 root     root         8192 Sep  2 12:43 tmp
    drwxr-xr-x   14 root     root         4096 Jul 15 12:50 usr
    drwxr-xr-x   16 root     root         4096 Jul  9 16:22 var

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

  • d - каталог ;
  • b - блочный специальный файл ;
  • c - символьный специальный файл ;
  • l - символьная ссылка ;
  • p - канал ;
  • - (минус) - обычный файл.
  • Отметим, что стандартом POSIX-2001 не предусмотрено обозначение для сокетов, указано только, что реализации могут вводить свои типы файлов и обозначения для них.

    Девять последующих символов отражают режим доступа к файлу: первые три символа - права доступа его владельца, следующие три - владеющей группы, последние три - права доступа прочих пользователей. Наличие буквы r ( чтение ), w ( запись ) или x (выполнение) означает, что соответствующее право имеется; знак минус свидетельствует об отсутствии права. Например, файл   /usr является каталогом, куда может писать только пользователь root, а читать и искать информацию - все остальные.

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

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

    Наконец, следуют дата и время последнего изменения и имя файла. По умолчанию имена файлов сортируются в алфавитном порядке.

    Опишем еще несколько употребительных опций служебной программы ls.

    Ключ Описание
    -a Вывести список всех файлов (обычно не выводятся данные о файлах, имена которых начинаются с точки).
    -C Выдавать имена файлов в несколько столбцов (с сортировкой по столбцам). Отметим, что если в командной строке встречаются пары взаимоисключающих опций (например, -lC), то действует та, что указана последней.
    -d Трактовать каталоги наравне с файлами других типов. Часто используется с опцией -l для получения сведений о состоянии каталога.
    -F Выводить символ / после имен каталогов, * - после выполнимых файлов, | - после каналов, @ - после символьных ссылок.
    -i Выдавать порядковый номер файла в файловой системе (см. выше).
    -R Рекурсивно обойти встретившиеся подкаталоги.
    -r Изменить порядок сортировки файлов на обратный.
    -t Использовать в качестве первичного ключа сортировки время последнего изменения (сначала идут самые свежие файлы ); имя служит вторичным ключом. Обычно символьные ссылки трактуются утилитой ls наравне с файлами других типов, только на месте имени выводится комбинацияимя_файла-ссылки -> содержимое_файла-ссылки. Для получения информации о файле, на который указывает символьная ссылка, следует воспользоваться одной из следующих опций.
    -L Если символьная ссылка является аргументом утилиты ls или встретилась в процессе обхода файловой иерархии, выдавать информацию об указуемом файле, а не о ссылке. На месте имени выводится имя файла -ссылки (а не указуемого файла ).
    -H Аналогична -L, но воздействует только на символьные ссылки, заданные в командной строке и указывающие на каталог.

    Приведем еще несколько примеров употребления утилиты ls. После выполнения команд, показанных в листинге 4.4, наша Linux -система выдает информацию определенного вида (см. листинг 4.5):

    cd /usr/share/zoneinfo/posix
    ls -il UTC Universal Zulu
    2506763 -rw-r--r--    6 root     root           56 Apr 15  2002 UTC
    2506763 -rw-r--r--    6 root     root           56 Apr 15  2002 Universal
    2506763 -rw-r--r--    6 root     root           56 Apr 15  2002 Zulu

    Как и предписывает опция -i, выданы порядковые номера ( уникальные идентификаторы ) файлов. Мы видим, что для всех трех имен они одинаковы (2506763), т. е. эти имена ссылаются на один и тот же файл. Поскольку выданное утилитой ls число ссылок равно шести, в файловой системе имеются еще три (жесткие) ссылки на данный файл.

    Полезно сопоставить команды и результаты, показанные в листингах 4.4 и 4.5, с теми, что приведены в листингах 4.6 и 4.7.

    cd /bin
    ls -il ex rvi rview vi view
    4849722 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 ex -> vi
    4849723 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 rvi -> vi
    4849724 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 rview -> vi
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 vi
    4849726 lrwxrwxrwx    1 root     root            2 Jul  6 14:17 view -> vi

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

    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 ex
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 rvi
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 rview
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 vi
    4849725 -rwxr-xr-x    1 root     root       386120 Mar  8  2002 view

    Данные о специальных файлах (см. листинг 4.9) могут выглядеть так, как показано в листинге 4.10.

    ls -l /dev/ttyp[0-3] /dev/fd[2-5]
    brw-rw----    1 root     floppy     2,   2 Apr  1  2002 /dev/fd2
    brw-rw----    1 root     floppy     2,   3 Apr  1  2002 /dev/fd3
    brw-rw----    1 root     floppy     2, 128 Apr  1  2002 /dev/fd4
    brw-rw----    1 root     floppy     2, 129 Apr  1  2002 /dev/fd5
    crw-rw-rw-    1 root     root       3,   0 Sep  2 17:21 /dev/ttyp0
    crw-rw-rw-    1 root     tty        3,   1 Apr  1  2002 /dev/ttyp1
    crw-rw-rw-    1 root     tty        3,   2 Apr  1  2002 /dev/ttyp2
    crw-rw-rw-    1 root     tty        3,   3 Apr  1  2002 /dev/ttyp3

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

    Сведения о файлах- каналах могут выглядеть так, как показано в листинге 4.11.

    total 0
    prw--w----    1 root     root            0 Sep  2 10:42 xdmctl
    prw--w----    1 shmyrev  root            0 Sep  2 10:42 xdmctl-:0

    В листинге 4.12 приведен пример вывода информации о сокетах.

    srwx------    1 shmyrev  root            0 Sep  2 10:42 /dev/gpmctl
    srw-rw-rw-    1 root     root            0 Sep  2 10:42 /dev/log

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

    ls -tl /

    может выдать следующую информацию (см. листинг 4.13):

    total 338
    drwxrwxrwt   23 root     root         8192 Sep  2 16:26 tmp
    dr-xr-xr-x  143 root     root            0 Sep  2 14:42 proc
    drwxr-xr-x   60 root     root         4096 Sep  2 10:42 etc
    drwxr-xr-x   18 root     root        86016 Sep  2 10:42 dev
    drwxr-x---    5 root     root         4096 Sep  1 17:10 root
    drwxr-xr-x    8 root     root         4096 Aug  8 13:16 lib
    drwxr-xr-x    7 root     root         4096 Jul 23 13:09 opt
    drwxr-xr-x   20 root     root         4096 Jul 15 18:00 home
    drwxr-xr-x   14 root     root         4096 Jul 15 12:50 usr
    drwxr-xr-x    2 root     root         4096 Jul 11 17:38 sbin
    drwxr-xr-x   16 root     root         4096 Jul  9 16:22 var
    drwxr-xr-x    2 root     root         4096 Jul  8 13:32 bin
    drwxr-xr-x    4 root     root         1024 Jul  6 15:06 boot
    drwx------    2 root     root        16384 Jul  6 14:15 lost+found
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 cdrom
    drwxrwxr-x    2 root     root         4096 Jul  6 13:31 a
    drwxr-xr-x    5 root     root         4096 Jul  6 11:24 mnt
    drwxr-xr-x    6 root     root         4096 Feb 23  2003 dss
    drwxr-xr-x    2 root     root         4096 Apr  1  2002 misc
    drwxr-xr-x    2 root     root         4096 Jun 12  2001 initrd

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

    При программировании на языке C информацию о файлах получают с помощью функций семейства stat() (см. листинг 4.14).

    #include <sys/stat.h>
    int stat (const char *restrict path, 
              struct stat *restrict buf);
    #include <sys/stat.h>
    int fstat (int fildes, struct stat *buf);
    #include <sys/stat.h>
    int lstat (const char *restrict path, 
               struct stat *restrict buf);

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

    Функция stat() предоставляет информацию о поименованном файле: аргумент path указывает на маршрутное имя файла. Чтобы получить эти сведения, достаточно иметь право на поиск для всех компонентов маршрутного префикса. Функция fstat() сообщает данные об открытом файле, задаваемом дескриптором файла   fildes. Функция lstat() эквивалентна stat() за одним исключением: если аргумент path задает символьную ссылку, lstat() возвращает информацию о ней, а stat() - о файле, на который эта ссылка указывает.

    В случае нормального завершения результат функций семейства stat() равен 0.

    Аргумент buf является указателем на структуру типа stat, в которую помещается информация о файле. Согласно стандарту POSIX-2001, в ней содержатся по крайней мере следующие поля:

    dev_t    st_dev;   
    /* Идентификатор устройства, содержащего файл */
    ino_t    st_ino;   
    /* Порядковый номер файла в файловой системе */
    mode_t   st_mode;  
    /* Режим файла 
    nlink_t  st_nlink; 
    /* Число жестких ссылок на файл */
    uid_t    st_uid;   
    /* Идентификатор владельца файла */
    gid_t    st_gid;   
    /* Идентификатор владеющей группы */
    off_t    st_size;  
    /* Для обычных файлов и символьных ссылок - 
                          размер в байтах */
    /* Для файлов других типов значение этого поля 
                          неспецифицировано */
    time_t   st_atime; 
    /* Время последнего доступа */
    time_t   st_mtime; 
    /* Время последнего изменения файла */
    time_t   st_ctime; 
    /* Время последнего изменения статуса файла */

    Некоторые пояснения. Комбинация значений ( st_dev, st_ino ) должна однозначно определять файл в пределах объединенной (в том числе сетевой) файловой системы. Статус файла меняется, когда модифицируются его атрибуты (например, режим ), а не содержимое.

    В файле   <sys/stat.h> определена не только структура stat, но и константы, полезные для работы с битами режима. Так, S_IFMT выделяет тип файла, S_IFREG обозначает обычные файлы, S_IRWXU - биты режима доступа владельца и т.д.

    Приведем пример использования функций stat() и lstat() (см. листинг 4.15).

    /* Программа выдает информацию о файлах - аргументах командной строки */
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    
    /* Функция возвращает односимвольное обозначение типа файла */
    /* Для неизвестного типа возвращается 'u'                   */
    static char my_filetype (const mode_t mode) {
     switch (mode S_IFMT) {
     case S_IFDIR:
       return ('d');
     case S_IFBLK:
       return ('b');
     case S_IFCHR:
       return ('c');
     case S_IFLNK:
       return ('l');
     case S_IFIFO:
       return ('p');
     case S_IFREG:
       return ('-');
     case S_IFSOCK:
       return ('s');
     default:
       return ('u');
     }
    } 
    int main (int argc, char *argv[]) {
     struct stat buf;
     int i;
     for (i = 1; i < argc; i++) {
       if (stat (argv [i], buf)) {
         fprintf (stderr, "\nstat: не удалось получить информацию о файле %s\n", argv [i]);
         return (-1);
       }
    
       printf ("\nstat-информация о файле %s:\n", argv [i]);
       printf ("Тип: %c\n", my_filetype (buf.st_mode));
       printf ("Размер: %ld\n", buf.st_size);
     
       if (lstat (argv [i], buf)) {
         fprintf (stderr, "\nlstat: не удалось получить информацию о файле %s\n", argv [i]);
         return (-1);
       }
    
       printf ("\nlstat-информация о файле %s:\n", argv [i]);
       printf ("Тип: %c\n", my_filetype (buf.st_mode));
       printf ("Размер: %ld\n", buf.st_size);
     }
    
     return 0;
    }

    Возможный результат работы этой программы с аргументами /bin/view и /bin/vi показан в листинге 4.16.

    stat-информация о файле /bin/view:
    Тип: -
    Размер: 386120
    lstat-информация о файле /bin/view:
    Тип: l
    Размер: 2
    stat-информация о файле /bin/vi:
    Тип: -
    Размер: 386120
    lstat-информация о файле /bin/vi:
    Тип: -
    Размер: 386120

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

    df  [опция ...] [файл ...]

    или функциями fstatvfs() или statvfs() (см. листинг 4.17).

    #include <sys/statvfs.h>
    int fstatvfs (int fildes, 
                  struct statvfs *buf);
    int statvfs (const char *restrict path, 
                 struct statvfs *restrict buf);

    Утилита df выдает на стандартный вывод информацию об имеющемся пространстве и, возможно, некоторые другие данные. Если файлы-аргументы не указаны, поступают сообщения обо всех компонентах объединенной файловой системы.

    По умолчанию пространство измеряется в 512-байтных блоках; опция -k предписывает использовать блоки размером в килобайт. Подразумеваемый формат вывода не специфицирован, но должны становиться известными по крайней мере имя файловой системы и объем свободного пространства. Если задана опция -P, употребляется так называемый мобильный формат вывода с заголовком

    Filesystem  512-blocks  Used Available 
                           Capacity Mounted on

    и следующей построчной информацией о каждой файловой системе:

  • имя файловой системы ;
  • общее пространство ;
  • занятое пространство ;
  • свободное пространство ;
  • процент занятости ;
  • корень файловой системы ( точка монтирования в объединенной файловой системе ).
  • Возможный результат выполнения служебной программы df с опциями -kP приведен в листинге 4.18.

    Filesystem         1024-blocks      Used Available Capacity Mounted on
    /dev/sda3             75822432  43456504  28514348      61% /
    /dev/sda1                46636     21129     23099      48% /boot
    none                    257212         0    257212       0% /dev/shm
    /dev/fd0                  1424       392      1032      28% /a

    Пара функций statvfs() и fstatvfs() по интерфейсу аналогична функциям stat() и fstat(), только в выходную структуру (типа fstatvfs ) помещается информация не о файлах, а о файловых системах, содержащих эти файлы. Согласно стандарту POSIX-2001, в структуре statvfs должны присутствовать по крайней мере следующие поля:

    unsigned long f_bsize;    
    /* Размер блока файловой системы */
    unsigned long f_frsize;   
    /* Базовый размер блока файловой системы */
    fsblkcnt_t    f_blocks;   
    /* Общее число блоков базового размера в 
    файловой системе */
    fsblkcnt_t    f_bfree;    
    /* Общее число свободных блоков */
    fsblkcnt_t    f_bavail;   
    /* Число свободных блоков, доступных 
    непривилегированным процессам */
    fsfilcnt_t    f_files;    
    /* Общее число описателей файлов */
    fsfilcnt_t    f_ffree;    
    /* Общее число свободных описателей файлов */
    fsfilcnt_t    f_favail;   
    /* Число описателей файлов, доступных 
    непривилегированным процессам */
    unsigned long f_fsid;     
    /* Идентификатор файловой системы */
    unsigned long f_flag;     
    /* Битная шкала флагов */
    unsigned long f_namemax;  
    /* Максимальная длина имени файла */

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

    В листинге 4.19 приведен исходный текст программы, которая выдает сведения о файловых системах, содержащих заданные в командной строке файлы-аргументы.

    #include <sys/statvfs.h>
    #include <stdio.h>
    
    int main (int argc, char *argv[]) {
     struct statvfs buf;
     int i;
    
     for (i = 1; i < argc; i++) { 
       if (statvfs (argv [i], buf)) {
         fprintf (stderr, "\nstatvfs: не удалось получить информацию о файловой системе, содержащей файл %s\n", argv [i]);
         return (-1);
       }
    
       printf ("\nstatvfs-информация о файловой системе, содержащей файл %s:\n", argv [i]);
       printf ("Размер блока файловой системы: %ld\n", buf.f_bsize);
       printf ("Базовый размер блока файловой системы: %ld\n", buf.f_frsize);
       printf ("Общее число блоков базового размера в файловой системе: %ld\n", buf.f_blocks);
       printf ("Общее число свободных блоков: %ld\n", buf.f_bfree);
       printf ("Число свободных блоков, доступных непривилегированным процессам: %ld\n", buf.f_bavail);
       printf ("Общее число описателей файлов: %ld\n", buf.f_files);
       printf ("Общее число свободных описателей файлов: %ld\n", buf.f_ffree);
       printf ("Число описателей файлов, доступных непривилегированным процессам: %ld\n", buf.f_favail);
       printf ("Идентификатор файловой системы: %ld\n", buf.f_fsid);
       printf ("Битная шкала флагов: %lx\n", buf.f_flag);
       printf ("Максимальная длина имени файла: %ld\n", buf.f_namemax);
     }
    
     return 0;
    }

    Если смонтировать   файловую систему FAT на каталог   /a с опцией "только на чтение ", то результат выполнения приведенной программы с аргументами / и /a может выглядеть так, как показано в листинге 4.20.

    statvfs-информация о файловой системе, содержащей файл /:
    Размер блока файловой системы: 4096
    Базовый размер блока файловой системы: 4096
    Общее число блоков базового размера в файловой системе: 18955608
    Общее число свободных блоков: 7990010
    Число свободных блоков, доступных непривилегированным процессам: 7027115
    Общее число описателей файлов: 9633792
    Общее число свободных описателей файлов: 8259049
    Число описателей файлов, доступных непривилегированным процессам: 8259049
    Идентификатор файловой системы: 0
    Битная шкала флагов: 0
    Максимальная длина имени файла: 255
    
    statvfs-информация о файловой системе, содержащей файл /a:
    Размер блока файловой системы: 512
    Базовый размер блока файловой системы: 512
    Общее число блоков базового размера в файловой системе: 2847
    Общее число свободных блоков: 1960
    Число свободных блоков, доступных непривилегированным процессам: 1960
    Общее число описателей файлов: 0
    Общее число свободных описателей файлов: 0
    Число описателей файлов, доступных непривилегированным процессам: 0
    Идентификатор файловой системы: 0
    Битная шкала флагов: f
    Максимальная длина имени файла: 260

    Интерпретация полученных результатов предоставляется читателю.

    Служебная программа

    du  [опция ...] [файл ...]

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

    Выделим несколько других опций.

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

    Пример. В ответ на команду

    du -k /usr/local/man /bin/vi /bin/view

    может быть выдано следующее (см. листинг 4.21):

    1428    /usr/local/man/man1
    12  /usr/local/man/man5
    64  /usr/local/man/man7
    36  /usr/local/man/man8
    1544    /usr/local/man
    384 /bin/vi
    0   /bin/view

    Таким образом, иерархия файлов с корнем в /usr/local/man занимает около 1.5 Мб (почти все приходится на долю файлов подкаталога man1 ), обычный файл   /bin/vi - 384 Кб (читателю предлагается сопоставить эту величину и приведенный выше размер данного файла в байтах), символьная ссылка лишнего места не занимает.

    Изменение атрибутов файлов и текущей позиции в файловой иерархии

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

    cd  [-L | -P] [целевой_каталог]
    cd  -

    и функция chdir():

    #include <unistd.h>
    int chdir (const char *path);

    Команда cd без аргументов осуществляет переход в домашний каталог пользователя, заданный переменной окружения HOME. Если аргументом является минус, выполняются действия, показанные в листинге 4.22: осуществляется переход в каталог, ранее бывший текущим, и в случае успеха выводится его абсолютное маршрутное имя.

    cd "$OLDPWD"  pwd

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

  • если имя начинается с точки или точки-точки, перед ним подставляется значение переменной окружения $PWD и / ;
  • в других случаях вместо $PWD подставляются элементы списка, являющегося значением переменной окружения $CDPATH и устроенного аналогично $PATH ; процесс продолжается до тех пор, пока не получится существующий каталог ; в случае необходимости в последнюю очередь используется значение $PWD.
  • Затем выполняется раскрытие символьных ссылок и устранение имен "точка" и "точка-точка" ("точка" уничтожается вместе со следующим за ней символом /, "точка-точка" - вместе с предыдущим компонентом, отличным от "точки-точки", и символом / между ними). Опции команды cd влияют на порядок выполняемых действий. По опции -P сначала раскрываются символьные ссылки. Это значит, что "точка-точка" трактуется как физический надкаталог ( каталог, вышележащий по отношению к указуемому файлу ). При наличии (подразумеваемой) опции -L порядок действий обратный; в результате "точка-точка" обозначает логический надкаталог ( каталог, вышележащий по отношению к символьной ссылке, а не к указуемому файлу ).

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

    В случае успешной смены текущего каталога соответственно изменяются значения переменных окружения $OLDPWD ( текущий каталог непосредственно перед выполнением команды cd ) и $PWD ( текущий каталог после выполнения команды cd ).

    Рассмотрим пример выполнения команды cd с разными опциями (см. листинг 4.23).

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

    ls -dl /usr/tmp /var/tmp
    cd /usr/tmp
    pwd
    pwd -P
    cd ..
    pwd
    cd -
    cd -P ..
    pwd
    lrwxrwxrwx    1 root     root           10 Jul  6 14:16 /usr/tmp -> ../var/tmp
    drwxrwxrwt    5 root     root         4096 Sep  4 18:05 /var/tmp
    /usr/tmp
    /var/tmp
    /usr
    /usr/tmp
    /var

    Можно видеть, что /usr/tmp является символьной ссылкой на каталог   /var/tmp. При варьировании опций команды pwd   каталог   /usr/tmp по-разному отображается в качестве текущего (напомним, опция -P команды pwd вызывает раскрытие символьных ссылок ). По-разному срабатывает и команда cd с целевым каталогом "точка-точка", опцией -P и без таковой. В результате видно различие между физическим и логическим надкаталогами символьной ссылки.

    Для изменения атрибутов файлов служат утилиты

    chown  [-R] [-H | -L | -P ] владелец[:группа] 
           файл ...

    (смена владельца и, быть может, владеющей группы файла) и

    chmod  [-R] изменение_режима файл ...

    (модификация режима файла ), а также аналогичные им по назначению и именам функции (см. листинг 4.25).

    #include <unistd.h>
    int chown (const char *path, uid_t owner, 
               gid_t group);
    #include <unistd.h>
    int fchown (int fildes, uid_t owner, 
                gid_t group);
    #include <sys/stat.h>
    int chmod (const char *path, mode_t mode);
    #include <sys/stat.h>
    int fchmod (int fildes, mode_t mode);

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

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

    Опции -R, -H и -L имеют в целом тот же смысл, что и для утилиты ls (см. выше): первая предписывает рекурсивный обход встретившихся подкаталогов, две другие - выборочное или полное раскрытие символьных ссылок, указывающих на каталоги. Опция -P означает, что изменения относятся к самим символьным ссылкам.

    Если при использовании функций chown() и fchown() меняется лишь владелец, то аргумент group задается равным ( gid_t ) (-1); при смене только группы идентификатор владельца следует задать как ( uid_t ) (-1).

    Задавая аргумент изменение_режима служебной программы chmod   владельца файла обозначают буквой u, владеющую группу - буквой g, прочих пользователей - o. Добавлению прав соответствует знак +, их удаление помечается знаком -. Знак = обозначает буквальное задание прав (для указанной категории пользователей устанавливается указанный режим доступа ). После букв, определяющих категорию пользователей, и знака операции следуют сами добавляемые (удаляемые, устанавливаемые) режимы доступа - обычно r, w и/или x ; можно указать несколько подобных связок, разделяя их запятыми (без пробелов).

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

    chmod go-w,u+x myfile

    Как указывалось выше, для удобства программирования аналогичных действий на языке C в заголовочном файле   <sys/stat.h> определены константы, соответствующие битам режима файлов. Например, S_IRUSR соответствует праву на чтение для владельца, S_IRWXG охватывает все три бита режима доступа для владеющей группы и т.п. С помощью побитных операций с текущим режимом файла и этими константами можно сформировать требуемое значение аргумента mode функции chmod() (см. листинг 4.27).

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdio.h>
    /* Программа добавляет права на выполнение для файлов - аргументов командной строки */
    int main (int argc, char *argv[]) {
     struct stat buf;
     int i;
    
     for (i = 1; i < argc; i++) {
       if (stat (argv [i], buf)) {
         fprintf (stderr, "\nstat: не удалось получить информацию о файле %s\n", argv [i]);
         return (-1);
       }
     
       if (chmod (argv [i], buf.st_mode | S_IXUSR | S_IXGRP | S_IXOTH)) {
         fprintf (stderr, "\nchmod: не удалось изменить режим доступа к файлу %s\n", argv [i]);
         return (-1);
       }
     }
    
     return 0;
    }

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

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

    touch  [-am] [-r эталонный_файл | -t время]
           [-c] файл ...

    Опция -a предписывает модифицировать время последнего доступа, опция -m - время последнего изменения; если не задана ни одна из них, выполняется и то, и другое.

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

    [[CC]YY]MMDDhhmm[.SS]

    Пары цифр указывают, соответственно, на две старшие цифры года, две младшие цифры года, месяц в году, номер дня в месяце, час в дне, минуту в часе и секунду в минуте.

    (Любопытны правила формирования старших цифр года, если они опущены. Когда младшие цифры лежат в диапазоне от 69 до 99, подразумевается 19; в противном случае - 20. Несомненно, в будущих версиях стандарта данное соглашение изменится.)

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

    Приведем пример совместного использования служебных программ touch, chown и chmod. В процессе загрузки ОС Linux выполняются действия по инициализации файлов с информацией о пользовательских сеансах, подобные тем, что приведены в листинге 4.28.

    > /var/run/utmp
    touch /var/log/wtmp
    chown :utmp /var/run/utmp /var/log/wtmp
    chmod ug=rw,o=r /var/run/utmp /var/log/wtmp

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

    Часто touch применяют при работе с файлами-замками (см. листинг 4.29).

    start () {
     echo -n "Starting cupsd: "
     daemon cupsd
     RETVAL=$?
     echo
     [ $RETVAL = 0 ]  touch /var/lock/subsys/cups
     return $RETVAL
    }

    Создание, удаление, копирование и перемещение файлов

    Стандарт POSIX-2001 не требует наличия утилиты для создания   обычных файлов. Они появляются по мере необходимости как побочный продукт многочисленных служебных программ (например, утилиты копирования), поэтому сама постановка задачи - создать файл "просто так" - является отчасти надуманной. В то же время, если новый файл все-таки нужен, полезно иметь в виду возможность перенаправления вывода пустой команды, которая имеется в языке shell.

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

    #include <fcntl.h>
    int creat (const char *path, mode_t mode);

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

    Если файл, который пытаются создать при помощи creat(), уже существует, он опустошается (размер становится равным 0), а режим доступа и владелец не изменяются.

    В случае неудачи результат creat() равен -1, а внешней переменной errno присваивается код ошибки, позволяющий определить причину ее (ошибки) возникновения. Переменная errno, а также мнемоники для кодов ошибок определены в заголовочном файле   <errno.h>. Для формирования системного сообщения об ошибке можно воспользоваться функцией perror() (см. листинг 4.31), которая, опираясь на значение errno, помещает в стандартный протокол описание последней ошибки.

    #include <stdio.h>
    void perror (const char *s);

    Например, при первом выполнении программы, приведенной в листинге 4.32, в стандартный протокол может быть выдан соответствующий результат (см. листинг 4.33).

    #include <fcntl.h>
    #include <errno.h>
    #include <stdio.h>
    #include <limits.h>
    /* Программа пытается создавать в текущем  */
    /* каталоге файлы с именами g1, g2, ...,   */
    /* пока эти попытки не закончатся неудачей */
    int main (void) {
     int  n = 0;
     char name [PATH_MAX]; 
     do
       sprintf (name, "g%d", ++n);
     while (creat (name, (mode_t) 0) >= 0);
     perror ("CREAT failed");
     fprintf (stderr, "errno = %d\n", errno);
     fprintf (stderr, 
             "Неудача на файле номер %d\n", n);
     return 0;
    }
    CREAT failed: Too many open files
    errno = 24
    Неудача на файле номер 1022

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

    CREAT failed: Permission denied
    errno = 22
    Неудача на файле номер 1

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

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

    Рассмотренный в примере стиль уведомления о неудачном завершении общий для большинства функций. Неудача определяется возвращением результата, невозможного в другом случае (почти всегда это -1 или пустой указатель NULL ); код ошибки заносится в переменную errno. Разумеется, в реальных программах errno не выводят, а анализируют. В данном случае уместно было бы сравнивать errno с константами EACCES, EINVAL, EMFILE и т.д. (см. <errno.h> ).

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

    df = creat ("/tmp/sample", 
                S_IRWXU | S_IRWXG | S_IRWXO);

    Для создания (пустых) каталогов служит утилита

    mkdir  [-p] [-m режим_доступа]  каталог ...

    и функция mkdir()

    #include <sys/stat.h>
    int mkdir (const char *path, mode_t mode);

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

    Если нужен не один, а целая цепочка каталогов, следует воспользоваться опцией -p. Соответствующий пример приведен в листинге 4.36.

    mkdir -p work/tmp/save

    По этой команде в текущем каталоге появится подкаталог work, в нем - каталог   tmp, а уже в нем - каталог   save.

    Смысл аргументов функции mkdir() тот же, что и для creat(), в случае неудачи возвращается -1, но при нормальном завершении результат равен 0 ( mkdir() не оставляет новый каталог открытым). Перечисленные выше условия неудачного завершения creat() (кроме превышения максимального числа одновременно открытых файлов ) применимы и к mkdir().

    Интерфейс, сходный с mkdir, имеют утилита mkfifo для создания каналов:

    mkfifo  [-m режим_доступа] канал ...

    Заметим, что здесь отсутствует опция -p ) и одноименная функция (см. листинг 4.37).

    #include <sys/stat.h>
    int mkfifo (const char *path, mode_t mode);

    Создать новые ссылки ( жесткие или символьные) на файл позволяют служебная программа ln:

    ln  [-fs] исходный_файл целевой_файл
    ln  [-fs] исходный_файл ... целевой_каталог

    а также функции link() и symlink() (см. листинг 4.38).

    #include <unistd.h>
    int link (const char *source_path, 
              const char *target_path);
    #include <unistd.h>
    int symlink (const char *link_contents, 
                 const char *link_name);

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

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

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

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

    Опция -f позволяет замещать существующие элементы каталогов новыми (по умолчанию задание существующего файла в качестве целевого считается ошибкой).

    Примером смены содержимого символьной ссылки посредством служебной программы ln с опциями -s и -f может служить фрагмент действий при загрузке ОС Linux, приведенный в листинге 4.39.

    if [ -n "$mver" ]; then
     ln -sf /lib/modules/$mver\
            /lib/modules/default
    fi

    Для удаления файлов служат утилиты

    rm  [-fiRr] файл ...

    и

    rmdir  [-p] файл ...

    а также функции unlink(), rmdir() и remove() (см. листинг 4.40).

    #include <unistd.h>
    int unlink (const char *path);
    #include <unistd.h>
    int rmdir (const char *path);
    #include <stdio.h>
    int remove (const char *path);

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

    Опишем опции утилиты rm. Опция -i означает удаление файлов с предварительным запросом подтверждения.

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

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

    В общем случае подтверждение на удаление запрашивается при выполнении следующего условия:

  • (не задана опция -f) И
  • (((отсутствует право на запись в файл) И
  • (стандартный ввод назначен на терминал)) ИЛИ
  • (задана опция -i)))
  • Наконец, две эквивалентные "сверхмощные" опции -R и -r (первая предусмотрена стандартом POSIX, вторая поддерживается по историческим причинам) позволяют удалять каталоги со всем их содержимым (если, конечно, хватает прав). Так, в процессе загрузки некоторых Unix -систем выполняется команда, которая удаляет каталог   /tmp и все содержащиеся в нем файлы и подкаталоги:

    rm -rf /tmp

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

    Для удаления каталогов (правда, только пустых) рекомендуется использовать служебную программу rmdir (или одноименную функцию). Опция -p предписывает удалять всю цепочку каталогов, составляющих заданное маршрутное имя. Например, если в текущем каталоге есть подкаталог a, содержащий только пустой подкаталог b, то команда, показанная в листинге 4.41, удалит и b, и a.

    rm -p a/b

    Функция unlink() предназначена для удаления файлов, не являющихся каталогами, а функция remove(), имеющая в стандарте C99 [5], объединяет возможности функций rmdir() и unlink().

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

    #include <unistd.h>
    #include <sys/types.h>
    #include <stdio.h>
    #include <sys/stat.h>
    /* Программа выполняет обработку */
    /* с осторожным замещением       */
    /* основного файла рабочим       */
    #define MAIN_FILE "/home/galat/garb/temp/mfile"
    #define OLD_FILE  "/home/galat/garb/temp/ofile"
    #define WORK_FILE "/home/galat/garb/temp/wfile"
    int main (void) {
     /* Необходимые описания */
     /* . . .                */
     int work_success = 1;
    
     /* Выполнение операций над рабочим файлом */
     /* . . .                                  */
    
     /* В случае неудачи выдадим диагностическое сообщение и удалим рабочий файл */
     if (! work_success) {
       fprintf (stderr, "\nНеудачное завершение операций над рабочим файлом %s\n", WORK_FILE);
       unlink (WORK_FILE);
       return (-1);
     }
    
     /* Установим режим доступа к рабочему файлу,  */
     /* подходящие для последующего использования  */
     /* Пусть, например, это будет доступ для всех */
     /* только на чтение                           */
     chmod (WORK_FILE, S_IRUSR | S_IRGRP | S_IROTH);
    
     /* Удалим ранее сохраненную старую версию основного файла */
     unlink (OLD_FILE);
    
     /* Сохраним текущую версию основного файла */
     if (link (MAIN_FILE, OLD_FILE)) {
       perror ("Не удалось сохранить текущую версию основного файла");
       return (-1);
     }
    
     /* Удалим текущую версию основного файла */
     unlink (MAIN_FILE);
    
     /* Сделаем рабочий файл основным */
     if (link (WORK_FILE, MAIN_FILE)) {
       perror ("Не удалось сделать рабочий файл основным");
       /* Восстановим основной файл */
       link (OLD_FILE, MAIN_FILE);
       return (-1);
     }
    
     /* Удалим рабочий файл */
     unlink (WORK_FILE);
    
     return 0;
    }

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

    cp  [-fip] исходный_файл целевой_файл
    cp  [-fip] исходный_файл ... целевой_каталог
    cp  -R [-H | -L | -P] [-fip] исходный_файл 
        ... целевой_каталог

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

    При наличии опции -R выполняется рекурсивное копирование исходных файловых иерархий в целевой каталог со стандартной трактовкой символьных ссылок, управляемой опциями -H, -L и -P (см. выше, например, описание утилиты chown ). Если копирование производится в существующий каталог, имена копий образуются как конкатенация целевого каталога, символа / и маршрута относительно каталога, содержащего исходный_файл. Если в командной строке заданы два файла и целевой не существует, он создается и становится корнем иерархии-копии, то есть исходный_файл копируется непосредственно в него, а маршруты файлов нижележащих уровней считаются относительно исходного_файла.

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

    Опция -i влечет запрос подтверждения перед попыткой копирования в существующий файл.

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

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

    Синтаксис, с точностью до опций аналогичный двум первым формам cp, имеет вызов служебной программы перемещения файлов   mv:

    mv  [-fi] исходный_файл целевой_файл mv  
        [-fi] исходный_файл ... 
    	существующий_целевой каталог

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

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

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

    Приведем пример употребления утилиты mv. Пусть текущий каталог содержит только файлы   x и y и пустой каталог   d1 (см. листинг 4.43). Тогда после выполнения команд, показанных в листинге 4.44, будет создан каталог   d2, где окажутся файлы   x и y, а каталог   d1 исчезнет (см. листинг 4.45).

    .: d1/  x  y
    ./d1:
    ls -RF
    mv x y d1
    mv d1 d2
    ls -RF
    .:
    d2/
    ./d2:
    x  y

    Продолжим этот пример двумя одинаковыми командами копирования (см. листинг 4.46).

    cp -R d2 d1
    cp -R d2 d1
    ls -RF

    Первая команда скопирует иерархию с корнем d2 во вновь созданный каталог   d1, вторая - под d1 (с сохранением имени d2 для корня копии). Результат показан в листинге 4.47.

    .:
    d1/  d2/
    ./d1:
    d2/  x  y
    ./d1/d2:
    x  y
    ./d2:
    x  y

    Для перемещения (переименования) одного файла служит функция rename() из репертуара C99 (см. листинг 4.48).

    #include <stdio.h>
    int rename (const char *old_path, 
                const char *new_path);

    Функция rename() обладает частью функциональности служебной программы mv. Она не перемещает файловых иерархий и не раскрывает символьных ссылок, если их имена заданы в качестве аргументов. С другой стороны, rename() поддерживает переименование каталогов. В этом случае аргумент new_path не должен задавать файл другого типа, а если он задает существующий каталог, тот, по понятным причинам, должен быть пустым.

    В продолжение двух предыдущих примеров продемонстрируем переименование каталогов (см. листинг 4.49). Итоговое состояние текущего каталога показано в листинге 4.50.

    #include <stdlib.h>
    #include <stdio.h>
    #define OLD_DIR "d1/d2"
    #define NEW_DIR "d2"
    int main (void) {
     system ("rm -f " NEW_DIR "/*");
     if (rename (OLD_DIR, NEW_DIR)) {
       perror ("RENAME failed");
       return (-1);
     }
     system ("ls -RF");
     return 0;
    }
    .:
    d1/  d2/
    ./d1:
    x  y
    ./d2:
    x  y

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

    Обход и обработка файловых иерархий

    Для обхода файловой иерархии и систематической обработки ее элементов служит утилита find:

    find  [-H | -L] файл ... [выражение]

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

    Перечислим элементарные логические выражения и правила их вычисления. (Далее n обозначает целое десятичное число; вместо него могут указываться также комбинации +n и -n, что означает, соответственно, "больше, чем n" и "меньше, чем n".)

    -name шаблон_файлов

    Истина, если имя текущего файла удовлетворяет шаблону_файлов. Символы шаблона, имеющие для shell специальный смысл, должны быть экранированы.

    -type тип_файла

    Истина, если файл имеет заданный тип: b, c, d, f, p или s - является, соответственно, блочным или символьным специальным файлом, каталогом, обычным файлом, каналом или сокетом.

    -size n[c]

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

    -atime n

    Истина, если последний доступ к файлу производился n дней назад (в данном контексте день - это промежуток времени в 86400 секунд).

    -mtime n

    Истина, если файл последний раз модифицировался n дней назад.

    -ctime n

    Истина, если атрибуты файла последний раз изменялись n дней назад.

    -perm [-]режим

    Истина, если режим файла соответствует заданному. При наличии знака минус соответствие понимается как включение (все заданные биты должны присутствовать в режиме доступа к файлу ); если минус отсутствует, требуется точное совпадение. Режим задается аналогично утилите chmod.

    -links n

    Истина, если на файл имеется n жестких ссылок.

    -user имя_пользователя

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

    -nouser

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

    -group владеющая_группа

    Истина, если файлом владеет заданная группа.

    -nogroup

    Истина, если идентификатор владеющей группы файла отсутствует в базе данных групп.

    -depth

    Всегда истина; задает дисциплину обхода иерархии вглубь: сначала обрабатываются все элементы каталога, потом - сам каталог (по умолчанию find в первую очередь обрабатывает каталог и лишь после этого - его элементы ).

    -xdev

    Всегда истина; предписывает не спускаться в каталоги, имеющие другой идентификатор устройства ( st_dev, см. выше описание структуры stat ).

    -prune

    Всегда истина; предписывает не обрабатывать текущий файл, если он является каталогом.

    -exec команда

    Истина, если после выполнения команды возвращается нулевой код завершения. запись команды должна заканчиваться экранированной точкой с запятой. Аргумент команды, заданный в виде пары фигурных скобок {}, заменяется маршрутным именем обрабатываемого файла.

    -ok команда

    Эквивалентно -exec за исключением того, что перед выполнением команды запрашивается подтверждение (в виде сгенерированной командной строки со знаком вопроса в конце), и она выполняется только при ответе y.

    -print

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

    -newer файл

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

    ( выражение )

    Истина, если истинно заключенное в скобки выражение (скобки должны быть экранированы от интерпретации языком shell).

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

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

    find . -name \*.c -print | wc -l

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

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

    cat `find . -name \*.c -print` | wc -l

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

    find . -name \*.c -exec cat {} \; | wc -l

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

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

    wc -l `find . -name \*.c -print`

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

    find . \( -size 0c -o -name \*.o -a -atime 
             +30 \) -ok rm {} \;

    Пусть на каталог   /mnt   смонтирован съемный носитель. Требуется выявить на нем файлы, владелец или группа которых на данной системе неизвестны, и изменить их на nobody. К цели ведет команда, показанная в листинге 4.52 (предполагается, что тот, кто ее выполняет, обладает соответствующими привилегиями).

    find /mnt \( -nouser -o -nogroup \) -exec 
         chown nobody:nobody {} \;

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

    Две команды, показанные в листинге 4.53, почти эквивалентны: обе не заходят в подкаталоги с именем skip   текущего каталога, но только вторая выводит их имена.

    find . -name skip -prune -o -print find 
         . -print -name skip -prune

    Одной из форм обхода и обработки файловой иерархии можно считать архивирование. Стандарт POSIX предусматривает для этого служебную программу pax:

    pax  [опция ...] [шаблон ...]
    pax  -r [опция ...] [шаблон ...]
    pax  -w [опция ...] [файл ...]
    pax  -r -w [опция ...] [файл ...] каталог

    Утилита pax поддерживает несколько архивных форматов, в число которых входят "родной" pax и унаследованные cpio и tar. В зависимости от заданной комбинации опций -r и -w pax осуществляет одну из четырех операций: выводит оглавление архива (первая из приведенных выше форм команды pax ), читает архив и извлекает из него файлы по заданным шаблонам имен (вторая форма), записывает заданные файлы в архив (третья форма) или копирует иерархии файлов (четвертая форма).

    Как правило, архив читается со стандартного ввода, а записывается на стандартный вывод; если встречается файл   типа " каталог ", обработке подвергается вся иерархия файлов с корнем в этом каталоге.

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

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

    В листинге 4.54 представлены примеры использования команды pax. Первая архивирует содержимое текущего каталога, последняя копирует каталог   old_dir в new_dir.

    pax -w .
    mkdir new_dir
    pax -rw old_dir new_dir
    Вернуться к учебному плану