PHPのgetopt関数をnodejsで使いたいと考えたのだが、githubでプログラム公開してくれている人もいる。
でも、なんか使いにくかったので、自分で使いやすいライブラリを作っておいた。
なんで使いにくいかというと、PHPのgetoptもそうなのだが、オプション指定するのが正直めんどくさい。
環境変数にあるデータをそのまま連想配列に落とし込んでくれるだけでいいのに、必須項目やら、指定keyやらを入れて返って来る値を取得する関数は正直めんどい。
ということで、引き渡し値の無い関数として作っておきました。
そして、key=value形式に対応しているものが無いのも少し不満だったので、これにも対応。
これなら、nodeで使う時に、requireしておくだけで、簡単に値を参照しやすいからね。
是非、コピーして使ってくだされ。
ソースコード
// getopt : argv情報を連想配列で返す
module.exports = (function(){
var argv = {argv:[] , options:{}};
var $$ = function(){
if(process.argv.length < 2){return {};}
for(var i=2; i<process.argv.length; i++){
var data = this.getString(process.argv[i]);
switch(data.type){
case "short":
var res1 = this.getString(process.argv[i]);
var next = (typeof process.argv[i+1] === "undefined") ? "" : process.argv[i+1];
var res2 = this.getString(next);
if(res2.type === "string"){
this.setOptions(res1.string , res2.string);
i++;
}
else{
this.setOptions(res1.string , true);
}
break;
case "short-multi":
var res = this.getString(process.argv[i]);
for(var j=0; j<res.string.length; j++){
var key = res.string.charAt(j);
if(key === ""){continue;}
this.setOptions(key , true);
}
break;
case "long":
// console.log(process.argv[i]);
var res1 = this.getString(process.argv[i]);
var next = (typeof process.argv[i+1] === "undefined") ? "" : process.argv[i+1];
var res2 = this.getString(next);
if(res2.type === "string"){
this.setOptions(res1.string , res2.string);
i++;
}
else{
this.setArgv(process.argv[i]);
}
break;
case "keyvalue":
// argv[data.key] = data.value;
this.setOptions(data.key , data.value);
break;
case "string":
console.log(process.argv[i]);
this.setArgv(data.string);
break;
}
}
};
$$.prototype.getString = function(str){
if(str === "" || str === "-" || str === "--"){
return {type:"" , string:""};
}
else if(str.match(/^\-\-(.+?)$/)){
return {type:"long" , string:RegExp.$1};
}
// short-single
else if(str.match(/^\-([0-9a-zA-Z])$/)){
return {type:"short" , string:RegExp.$1};
}
// short-multi
else if(str.match(/^\-([0-9a-zA-Z]+?)$/)){
return {type:"short-multi" , string:RegExp.$1};
}
// key-value
else if(str.match(/^(.+?)=(.+?)$/)){
return {type:"keyvalue" , key:RegExp.$1 , value:RegExp.$2};
}
else{
return {type:"string" , string:str};
}
};
$$.prototype.setOptions = function(key , value){
if(typeof argv.options[key] === "undefined"){
argv.options[key] = value;
}
else{
if(typeof argv.options[key] === "string"){argv.options[key] =[argv.options[key]];}
argv.options[key].push(value);
}
};
$$.prototype.setArgv = function(value){
if(value){
argv.argv.push(value);
}
}
new $$;
return argv;
})();
仕様
概要
基本的にnodejsを実行するコマンドラインの、環境変数部分を連想配列に落とし込むという処理。
コマンドサンプル
"node program.js [環境変数]"
環境変数サンプル
# option - short
-[0-9a-zA-Z] : "-" + 英数字1文字で構成されて、次のargvで値を入れなければtrueをoptionsに格納
# option - short-multi
-ABC : "-" + 英数字1文字を複数まとめて書く方法、ただし値は送れず、bool値(true)のみをoptionsに格納
# options - long
--[*string] : "--" + 任意文字列で指定して、値をoptionsに格納。(bool値は受け取れない)
# options - key-value
*key=*value : "="が入ったkey=value形式の文字列は、argvに入らずに、optionsに格納される。
# argv
指定無しの文字列 : argvに配列でargvに格納。
# 同一key値の場合
optionsで同じkey値がある場合は、配列にして値を返す。
使い方と実行テスト
実行サンプルコード
;(function(){
// "./~"が必要
var argv = require('./getArgv.js');
console.log(JSON.stringify(argv , null , " "));
})();
上記を実行すると以下の値が得られる。
$ node test.js foo -s --long-with-arg bar -m a -m b -- --others
foo
{
"argv": [
"foo",
"--others"
],
"options": {
"s": true,
"long-with-arg": "bar",
"m": [
"a",
"b"
]
}
}
想定通りに返って来る事を確認できたので、テスト完了とします。
簡単ソース解説
まず、requireした時点で帰る値がargvの成形された連想配列になっている点で、プログラム冒頭に1行下記を書いておくだけでいい。
var argv = require('./getArgv.js');
そして、返ってきた値は、argv内に、"argv"と"options"に振り分けられて格納されている。
ソースコードのポイントは、実際の環境変数の3番目からを全て順番になぞって行って処理を行なっている。
※環境変数の1番目はnodeプログラムファイルパスで、2番目は実行プログラムパスが入っている。
少しややこしかったのは、オプションの次に値が来るのか、bool値のみでの使用なのかを判定する為にfor文の次の値を検索しに行っている点だが、ここもルール化はされているので、コマンド実行の際に値が取れなければコマンドを見直す事で対して問題にならないだろう。
実際に書いて見るとかなり短いコードで完成できたので、他の言語でも便利に使えるようにローカライズしておきたいと思う。
ただし、今回はあまり厳密に文字コードをチェックしていないので、変な記号を織り交ぜて使うと、破綻してしまうかもしれないが、もし何か見つけた人はコメントをくだされ。
参考リンク
node-getopt
https://www.npmjs.com/package/node-getopt
PHPのgetoptの仕様
http://php.plus-server.net/function.getopt.html
0 件のコメント:
コメントを投稿