Wednesday, March 25, 2020 / Javascript, Node.js, DTP, Recursion

Node.js で 指定されたディレクトリ以下にある InDesign ファイルを サブディレクトリを含めてすべて取得

別に対象が InDesign ファイルに限った話ではないが、再帰的にディレクトリを検索して、すべての InDesign ファイルリストをつくる、 という必要が生じたので備忘録として書き残します. 基本的には 再帰を使って木構造をリストに変換する JavaScript で書いたのと同じです.

ディレクトリのみを収集する関数

  • fs.readdirSync(targetDirPath) すれば 対象ディレクトリパス(targetDirPath) にある ファイルまたはディレクトリのリストが得られる.
  • fs.statSync(targetPath).isDirectory() すれば それがディレクトリかどうか判別できる.

この知識からディレクトリリストを返す toDirList 関数:

const fs = require('fs');

const toDirList = (dir)=> {
    const subDirs = fs.readdirSync( dir ).map( (item)=> `${dir}/${item}` ).filter( (item)=> fs.statSync(item).isDirectory() )
    if( subDirs.length==0 ){
        return [dir];
    }

    return subDirs.reduce( (a,b)=> a.concat( toDirList(b) ), [dir]);
};

const dirList = toDirList('.');
console.log(dirList);

toDirList 関数内で、toDirList を再帰的に呼ぶことでサブディレクトリ、サブサブディレクトリ、サブサブサブディレクトリ…に対応.

ディレクトリ内のすべてのファイルを返す関数

  • fs.readdirSync(targetDirPath) すれば 対象ディレクトリパス(targetDirPath) にある ファイルまたはディレクトリのリストが得られる.
  • fs.statSync(targetPath).isFile() すれば それがファイルかどうか判別できる.

この知識からファイルリストを返す toFileList 関数:

const toFileList = (dir)=> {
    return fs.readdirSync( dir ).map( (item)=> `${dir}/${item}` ).filter( (item)=> fs.statSync(item).isFile() );
};

indd ファイルのみを返す関数

  • filePath.endsWith('.indd') して拡張子が indd か判定する.

この知識から indd ファイルのみを返す関数:

const toInddFileList = (dir)=> {
    return toFileList(dir).filter( (item)=> (item.endsWith('.indd')) );
};

結果

以上の関数全部をつないで完成させると:

const fs = require('fs');

const toDirList = (dir)=> {
    const subDirs = fs.readdirSync( dir ).map( (item)=> `${dir}/${item}` ).filter( (item)=> fs.statSync(item).isDirectory() )
    if( subDirs.length==0 ){
        return [dir];
    }

    return subDirs.reduce( (a,b)=> a.concat( toDirList(b) ), [dir]);
};

const toFileList = (dir)=> {
	return fs.readdirSync( dir ).map( (item)=> `${dir}/${item}` ).filter( (item)=> fs.statSync(item).isFile() );
};

const toInddFileList = (dir)=> {
    return toFileList(dir).filter( (item)=> (item.endsWith('.indd')) );
};


const targetDir = '.';

toDirList(targetDir).forEach( (dir)=> {
	toInddFileList(dir).forEach( (inddFile)=> {
		console.log(inddFile);
	});
});

更新) flatMap を使って 結果の書き出し部分を書き直した

手元で使っている node.js のバージョンでは flatMap にまだ対応していないようなので、 underscore の力を借りることにした.

_.flatten( toDirList(targetDir).map( (dir)=> toInddFileList(dir) ) ).forEach( (inddFile)=> {
    console.log(inddFile);
});

underscore を使うには:

npm init -y
npm install underscore

その後、コードの先頭に追加:

const _ = require('underscore');

まとめ

再帰を使えば割と簡単に書くことができるのだが、こんな面倒なことはライブラリを使って済ますべき話かも.

試してはいないが、たとえば recursive-readdir とか.