はじめに
こんにちは、たか(@HighHawk5)です。
最近、50GBのアクセスログファイルを解析したのですが、データサイズが大きすぎて普通のファイルシステムでは途中で処理落ちしてしまうんですね。
そこでNode.jsの非同期処理(ストリーム)を採用したところ、なんの問題もなく思うような処理をサクサクと行うことができたので、Node.jsのファイルシステムを使った、ローカルファイルをいろいろ操作できるスクリプト集を作りました。
ローカルファイルを一括で修正したり、解析したり、スクリプトを組み合わせて様々なファイル操作に活用してみてください。
すべてGitHubに上げておきましたので、ダウンロードしてすぐお使いになれます。
https://github.com/nkoutaka/nodeFileSystem
nodeFileSystem
├── changeLines.js
├── deleteFiles.js
├── grepFiles.js
├── listUpFiles.js
├── modules
│ └── fsCustom.js
├── readme.md
├── datas
└── results
以下、それぞれの機能を逆引き辞典風にご紹介します。
特定のディレクトリ配下にあるファイルをリストアップしたい
特定のディレクトリ配下に存在するファイルを再帰的に取得し、ファイル名(ファイルパス)のリストを作成するスクリプトです。
使用例:現行のWEBシステムで使用している画像・CSS・JSファイルがどこにどのくらいあるか(総数・場所・ファイル名)を調べる際に、実際のファイルベースでリストアップするために使用しました。
constfs=require('fs');/**
* 指定したディレクトリ配下のファイルを再帰的にリストアップする
* @param {string} dirPath 対象ディレクトリのフルパス
* @return {Array<string>} ファイルのフルパス
*/constlistFiles=dirPath=>{constfiles=[];constpaths=fs.readdirSync(dirPath);for(letnameofpaths){try{constpath=`${dirPath}/${name}`;conststat=fs.statSync(path);switch(true){casestat.isFile():files.push(path);break;casestat.isDirectory():files.push(...listFiles(path));break;default:}}catch(err){console.error('error:',e.message);}}returnfiles;};
fs.readdirSync(dirPath)
ではディレクトリ直下のファイル一覧しか取得できないため、取得したデータをディレクトリと判定した場合はさらに取得処理を繰り返すことで、再帰的なファイル取得を実現しています。
特定のファイルが存在するか確かめたい
指定したパスのファイルが存在するか確認し、boolean値を返すスクリプトです。fs.statSync(filePath)
は対象バスが存在しない場合にエラーを返すため、try構文でラップしてあります。
使用例:用意したファイル一覧に従って各ファイルが存在するかしないかを調べる際に使用しました。
constfs=require('fs');/**
* 指定したフルパスのファイルが存在するか確認する
* @param {string} filePath 対象とするファイルのフルパス
* @return {boolean}
*/constisExistFile=filePath=>{try{fs.statSync(filePath);returntrue}catch(err){if(err.code==='ENOENT')returnfalse}};
特定のファイルを削除したい
指定したパスのファイルを削除するスクリプトです。
削除するパスのリストファイル(例:./data/deleteFiles.csv
)を用意して、一括削除もできます。
使用例:用意したファイル一覧に従って各ファイルを一括削除する際に使用しました。
constpath=require('path');constfs=require('fs');constfsCustom=require('./modules/fsCustom');//削除するファイルのパスconsttargets=['/hoge/hoge.html','/hoge/hoge2.html','/hoge/hoge3.html'];//削除するパスのリストファイル//const datas = fs.readFileSync('./data/deleteFiles.csv', 'utf8');//const targets = datas.split(/\n/);for(consttargetoftargets){constfilePath=path.resolve(__dirname,target);try{if(fsCustom.isExistFile(filePath)){fs.unlinkSync(filePath);console.log(`${filePath}を削除しました。`);}}catch(err){console.error('error:',err.message);}};
データサイズの大きなファイルを一行ずつ取得して修正したい
特定のディレクトリ配下にあるファイルを再帰的に取得し、一行ずつ修正していくスクリプトです。
例えば、一行ずつ正規表現で文字列を修正していき、それを一つのファイルにまとめるなど。修正処理部分は適宜変更してお使いください。
使用例:普通に開いたら落ちてしまうほど大きいサイズのファイルから必要な文字列だけを一行ずつ抽出して、新たに小さなサイズのファイルとしてまとめる際に使用しました。
constpath=require('path');constfs=require('fs');constfsCustom=require('./modules/fsCustom');constreadline=require('readline');//修正するファイルがあるディレクトリconstdirPath=path.resolve(__dirname,'./datas');constfiles=fsCustom.listFiles(dirPath);for(constfileoffiles){conststream=fs.createReadStream(file,'utf8');constreader=readline.createInterface({input:stream});reader.on('line',(data)=>{//修正処理constmatch=data.match(/ ("[A-Z]{3,7}\/.+?) .+?" [0-9]{3} /);if(match&&match[1]){fs.appendFileSync(`./results/changeLines.csv`,`${match[1]}"\n`,(err,data)=>{if(err)console.log(err);elseconsole.log('write end');});}});}
キーワードが含まれるファイルをリストアップしたい
特定のディレクトリ配下にあるファイルを再帰的に取得し、各キーワードが含まれるファイルをリストアップするスクリプトです。
使用例:現行のWEBシステムで「不要な画像ファイル」を探し出すために使用しました。CSS・JS・DOMファイルの中身を再帰的に検索して、画像ファイル名が全く含まれていない場合は不要と判定できます。
constpath=require('path');constfs=require('fs');constfsCustom=require('./modules/fsCustom');//検索ワードの配列constkeywords=['/hoge/hoge.html','/hoge/hoge2.html','/hoge/hoge3.html']//検索ワードのリストファイル//const datas = fs.readFileSync('./data/grepFiles.csv', 'utf8');//const keywords = datas.split(/\n/);//検索対象のディレクトリconstdirPath=path.resolve(__dirname,'./');constfiles=fsCustom.listFiles(dirPath);for(letkeywordofkeywords){constresults=[keyword];keyword=keyword.replace(/\./g,'\\.');for(constfileoffiles){//正規表現で検索ワードを生成letregex=`("|'|\\|)(@\\{)?${keyword}.*`;letdata=fs.readFileSync(file,'utf8');letcount=(data.match(newRegExp(regex,'g'))||[]).length;if(count){results.push(file);}};fs.appendFileSync(`./results/grepFiles.csv`,results.join(',')+'\n',(err,data)=>{if(err)console.log(err);elseconsole.log('write end');});};
まとめ
大きなデータサイズのファイルを処理する場合はメモリリークを気にする必要があります(JavaScriptの変数に格納できるデータ量は1GBほどでした)。
その際はぜひ、Node.jsの非同期処理を活用してみましょう。
GitHub:https://github.com/nkoutaka/nodeFileSystem