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

В основном, эту информацию можно получить из структуры inode, но не в Node.js под Apple.

Прежде чем попробовать заново реализовать UNIX команду ls, давайте посмотрим, что мы можем узнать про элемент файловой системы с помощью Node.js

Библиотека fs, поставляющаяся вместе с Node.js, предоставляет асинхронный метод stat, который получает первым аргументом путь к элементу файловой системы, находит информацию о нем в inode, затем вызывает функцию-коллбек (обратный вызов, переданный вторым аргументом). В коллбек будет передан объект fs.Stats.

Так же есть синхронная версия этого метода statSync, которая вернет объект fs.Stats, когда будет завершено чтение информации из файловой системы.

Эта программа показывает, как использовать асинхронный метод:

examples/node/stats.js

var fs = require('fs');

if (process.argv.length <= 2) {
    console.log("Usage: " + __filename + " path/to");
    process.exit(-1);
}

var path = process.argv[2];

fs.stat(path, function(err, stats) {
    console.log(path);
    console.log();
    console.log(stats);
    console.log();

    if (stats.isFile()) {
        console.log('    file');
    }
    if (stats.isDirectory()) {
        console.log('    directory');
    }

    console.log('    size: ' + stats["size"]);
    console.log('    mode: ' + stats["mode"]);
    console.log('    others eXecute: ' + (stats["mode"] & 1 ? 'x' : '-'));
    console.log('    others Write:   ' + (stats["mode"] & 2 ? 'w' : '-'));
    console.log('    others Read:    ' + (stats["mode"] & 4 ? 'r' : '-'));

    console.log('    group eXecute:  ' + (stats["mode"] & 10 ? 'x' : '-'));
    console.log('    group Write:    ' + (stats["mode"] & 20 ? 'w' : '-'));
    console.log('    group Read:     ' + (stats["mode"] & 40 ? 'r' : '-'));

    console.log('    owner eXecute:  ' + (stats["mode"] & 100 ? 'x' : '-'));
    console.log('    owner Write:    ' + (stats["mode"] & 200 ? 'w' : '-'));
    console.log('    owner Read:     ' + (stats["mode"] & 400 ? 'r' : '-'));


    console.log('    file:           ' + (stats["mode"] & 0100000 ? 'f' : '-'));
    console.log('    directory:      ' + (stats["mode"] & 0040000 ? 'd' : '-'));



});


Предполагается, что эта программа вызывается таким образом: node examples/node/stats.js path/to/file.

Для примера я запустил node examples/node/stats.js examples (указав в качестве параметра директорию 'examples') и получил такой результат:

examples

{ dev: 16777220,
  mode: 16877,
  nlink: 11,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 32548075,
  size: 374,
  blocks: 0,
  atime: Sat Jan 31 2015 10:56:30 GMT+0200 (IST),
  mtime: Sat Jan 31 2015 10:52:13 GMT+0200 (IST),
  ctime: Sat Jan 31 2015 10:52:13 GMT+0200 (IST) }

    directory
    size: 374
    mode: 16877
    others eXecute: x
    others Write:   -
    others Read:    r
    group eXecute:  x
    group Write:    w
    group Read:     r
    owner eXecute:  x
    owner Write:    w
    owner Read:     r
    file:           -
    directory:      d

Сравним полученную информацию с результатом работы команды ls:

$ ls -ld examples
drwxr-xr-x  11 gabor  staff  374 Jan 31 10:52 examples

Давайте разберем нашу программу:

var fs = require('fs');

if (process.argv.length <= 2) {
    console.log("Usage: " + __filename + " path/to");
    process.exit(-1);
}

var path = process.argv[2];

После загрузки библиотеки fs мы проверяем количество аргументов, полученных через командную строку. Если их 2 или меньше (я не уверен, что меньше двух вообще возможно), значит пользователь вообще не передал никаких аргументов. (Если пользователь выполнил node examples/node/stats.js, тогда мы получим два аргумента). В этом случае мы выводим пользователю подсказку:

$ node examples/node/stats.js
Usage: /home/gabor/code-maven/examples/node/stats.js path/to

Глобальная переменная __filename (начинается с двух подчеркиваний) содержит полный путь к текущему выполняемому файлу. Затем вызываем process.exit() для немедленного завершения работы.

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

Вызов fs.stat

Затем мы вызвали метод stat, передав в него path и функцию-коллбек. Эта функция получит объект ошибки (если она случится) и объект fs.Stats

fs.stat(path, function(err, stats) {

Объект Stats содержит данные, полученные из inode (в нашем случае это выглядит так:)

{ dev: 16777220,
  mode: 16877,
  nlink: 11,
  uid: 501,
  gid: 20,
  rdev: 0,
  blksize: 4096,
  ino: 32548075,
  size: 374,
  blocks: 0,
  atime: Sat Jan 31 2015 10:56:30 GMT+0200 (IST),
  mtime: Sat Jan 31 2015 10:52:13 GMT+0200 (IST),
  ctime: Sat Jan 31 2015 10:52:13 GMT+0200 (IST) }

Кроме этого, Stats предоставляет несколько методов для большего удобства.

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

mode содержит много информации, включая тип элемента (файл/директория/символьная ссылка) и права доступа.

uid - идентификатор владельца элемента.

gid - идентификатор группы владельца.

size - размер в байтах.

atime, mtime, и ctime - время последнего доступа, изменения и создания элемента.

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

isFile() вернет True, если это файл.

isDirectory() вернет True, если это директория.

В документации по fs.Stat перечислены еще несколько подобных функций.

Обратиться к остальным значениям можно обычным для JavaScript способом: К примеру, вот так можно получить значение для 'size':

Размер файла в Node.js

console.log('    size: ' + stats["size"]);

Права доступа к файлу

Раздел документации man 2 stat описывает, как трактовать значение mode, которое в нашем случае равно 16877.

Нам нужно использовать специальные битовые маски, чтобы определить, установлены ли нужные нам биты в 0 или в 1. Например, mode & 1 будет равно 1, если правый бит в mode установлен в 1.

mode & 2 будет равно 2, если второй бит справа установлен в 1, и 0, если это не так.

mode & 4 будет равно 4, если третий (!) бит справа установлен в 1, и 0, если нет.

К счастью JavaScript все числа, кроме 0, рассматривает как True. Таким образом, мы можем использовать тернарный оператор ?: для возврата каких-нибудь подходящих символов, если значение выражения отлично от 0, и возвращать - если оно равно 0.

Таким образом, мы можем выводить символы rwx- по аналогии с командой ls -ld.

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