ITFFFとかいうサービスを使ってみようと思い、ブログに投稿するとFacebookにも自動で投稿するように設定してみました。
なので久しぶりに技術ブログを更新。
JavaのRuntime.exec(String cmd)メソッドはとっても便利なのですが、cmd文字列の中のファイル名やコマンドオプション文字列にスペースが入っている場合に動作がおかしくなります。
cmd="ls *.bat";
たとえば、上記のパラメーターでRuntime.exec()を呼び出すときちんと動きます。
cmd="sh -c \"ls *.bat\"";
しかし、上記のパラメーターのようにコマンド文字列に'"'でくくった部分がある場合、Runtime.exec()を呼び出すとエラーになてしまい、きちんと動きません。
C言語とか他の言語では動くのに。
ネットの一部の掲示板ではshコマンドのパスが悪いとかいろいろ書かれていますが、どれも原因がいまいち。しかもWindowsではきちんと動くのですが、MacやLinuxでは動かないのです。
そこで、動かない原因を解析しました。
JAVAの仕様書を見ると、Runtime.exec(String cmd)はStringTokenizerを使ってRuntime.exec(String[] cmd)に分解されると書かれています。
StringTokenizerは上記の文字列を以下のように分解するようです。
String[] cmd={"sh","-c","\"ls","*.bat\""};
StringTokenizerはスペースがくるとそこで文字列を強制分割してしまうので、'"'でくくった部分を正しく解釈してくれないようです。
似たようなメソッドで文字列の配列を渡すRuntime.exec(String[] cmd)メソッドならばスペースがあっても大丈夫なのですが、Runtime.exec(String cmd)メソッドはスペース対応してないので、仕様書を見る限り、正しく動作しないようです。
Runtime.exec()メソッドにStringの配列で渡せばよいのですが、関数の戻り値とかをRuntime.exec()メソッドに渡す場合、配列だといろいろめんどくさいしなぁ。
他の言語ではだいたいどれもできるのに、Javaだけできないなんて。うーん。困った。
そこで今日一日よーく考えて、StringTokenizerの代わりとなる、execTokenizerメソッドを作ってみました。
これがあれば、こんな感じでスペースやリダイレクトがある文字列でも正しく動きます。
Runtime.exec(execTokenizer("sh -c \"ls *.bat\""))
急いで書いたのでexecTokenizer()がまだ汚い。
原理は確認できたのであとでソースをきれいにしよう。
----------------
public static String[] execTokenizer(String cmd)
{
int j,i,l,c,lc=' ',ct=0;
String[] ret=null;
int hazime=0,owari=0;
if(cmd==null)return null;
try{
//count umber
l=cmd.length();
for(i=0;i<l;lc=c,i++){
c=cmd.charAt(i);
if(c=='\"'){
for(j=i+1;j<l;lc=c,i++,j++){
c=cmd.charAt(j);
if(c=='\"'){
ct++;
break;
}
}
}
if(c==' ' && lc!=' '){
ct++;
}
}
if(lc!=' ' && lc!='\"'){
ct++;
}
if(ct==0){
return ret;
}
ret=new String[ct];
ct=0;
//
l=cmd.length();
for(i=0;i<l;lc=c,i++){
c=cmd.charAt(i);
if(c!=' ' && lc==' '){
hazime=i;
}
if(c=='\"'){
hazime=i+1;
for(j=i+1;j<l;lc=c,i++,j++){
c=cmd.charAt(j);
if(c=='\"'){
owari=j;
ret[ct]=cmd.substring(hazime,owari);
ct++;
break;
}
}
}
if(c==' ' && lc!=' '){
owari=i;
ret[ct]=cmd.substring(hazime,owari);
ct++;
}
}
if(lc!=' ' && lc!='\"'){
owari=i;
ret[ct]=cmd.substring(hazime,owari);
ct++;
}
}catch(Exception e){};
return ret;
}
0 件のコメント:
コメントを投稿