Javaでshellスクリプトと問題のまとめを呼び出して実行する方法
10330 ワード
この文章はアリババ技術協会(ATA)の精選集から来ている.
背景
我々は開発過程でjava開発が大部分であり,テキスト処理過程では主にスクリプト開発が行われている.JAva開発の特徴は、TDDL、METAQなどのドッキングを早く行うことができることです.スクリプト開発の特徴は,バッチ処理を行う際に非常に便利であることである.先日、キャプチャしたデータをパッケージ化し、nodejsの下でphantomjsベースのcasperjs爬虫類などのスクリプトでキャプチャする必要があるシーンに遭遇しました.
解決策
最初の問題:javaがキャプチャし、結果をパッケージ化します.直接的な方法はJAvaは各種メッセージを受信する(db,metaqなど)を使用し、jstormクラスタを介してスケジューリングおよびキャプチャを行います.最後にキャプチャの結果を1つのファイルに保存し、shellを呼び出してパッケージングし、返信します.java呼び出しodpsを直接ファイルに保存しない理由という質問があるかもしれません.答えは、私たちのクラスタはhzクラスタではなくodpsを直接アップロードする速度に問題があるので、先にパッケージングします適当です.(ここはデザインにこだわらず、本題に戻ります)
JAvaがshellを呼び出す方法
プロセスビルダーによるスケジューリング
この方法は直感的で、パラメータの設定も便利です.例えば、私が実践しているコード(ビジネスコードの一部を隠しています):
ここではいくつかのパラメータを説明する必要があります:RUNNING_SHELL_FILE:実行するスクリプトSHELL_FILE_DIR:実行するスクリプトがあるディレクトリ;もちろん、実行するスクリプトをフルパスに書くこともできます.runningStatus:運転状態、0は正常です.詳細はjavaドキュメントを参照してください.param 1,param 2,param 3:RUNNINGでできるSHELL_FILEスクリプトで直接1,2,$3でそれぞれ得られるパラメータ.
直接システムRuntimeでshellを実行
この方法は暴力的で、よく使われています.コードは以下の通りです.
Runtime方式ではbuilderほど便利ではないことが分かった.特にパラメータの面では、execが文字列全体をshellとして実行するため、自分でスペースを付けなければならない.
存在する可能性のある問題と解決方法
上を通ることであなたのニーズを満たすことができると思ったら、壁にぶつかるかもしれません.次のような状況に遭遇します.
実行権限なし
この状況で私たちのチームの朱東方は出会って、DTSの移行をする過程で、パッケージの中のshellスクリプトを実行して、解凍してから、実行できないことに気づきました.では、上記の方法で授権しましょう.
JAvaはshellの戻りを待つ
この問題はもっとよく出会うだろう.なぜなら、shellスクリプトにechoまたはprint出力があり、バッファが切れてしまったからです.このような状況を避けるために、バッファを必ず読んで、shellの具体的な運転状態をlogすることができるというメリットがあります.例えば、上記の例では、
start()の後、waitFor()の前にバッファを読み出してlogを打つと、shellが予想通りに実行されていない理由がわかります.これはshellから出力された結果を読むことができ、javaコードのさらなる操作を容易にするというメリットがあります.
手作業で実行できるコマンドなのに、javaが呼び出したshellのコマンドが実行できないという問題に直面するかもしれません.エラー:コマンドは存在しません.
例えばcasperjsを使っているとき、shellを手動で実行するのは実行できるのにjava呼び出しのとき、いつもエラーが発生していることに気づきました.バッファを読み込むことでエラーログが見つかります.インストールしたcasperjsのbinをpathに追加しても十分ではないことに気づきました(/etc/profile、各種bashrc)
Javaがshellを呼び出すとき、デフォルトではシステムの/bin/下の命令が使われていたからです.特にroot権限で実行しているとき.この時、あなたは/binの下にソフトチェーンを追加します.私の上記の例では、/binの下にソフトチェーンを追加します.
これで、問題は解決できます.
Java呼び出しshellでパッケージ化されている場合は、パスの問題に注意してください.
shellのtarの圧縮と解凍は直接書くことができません.
tar -zcf/home/admin/data/result.tar.gz/home/admin/data/result
直接エラーを報告します.tarの圧縮源はパスの下に行かなければならないので、書くことができます.
tar -zcf/home/admin/data/result.tar.gz -C/home/admin/data/result
もし私のshellがjarパッケージにいたらどうしますか?
答えは:解凍します.上記の指示に従って操作します.(1)経路を見つける
ファイルの処理方法は簡単で、直接touchの一時ファイルをtouchして、それからデータストリームを書き込んで、コード:
これがあれば、踏み込みを減らし、javaとスクリプトのインタラクションを大胆に使うことができると信じています.JAvaはshellを呼び出すことができるので、shellは他のものを呼び出すのが便利です.バッファに過度に依存してスレッド間の通信を行わないでください.原因は自分で勉強しましょう.
これが皆さんの役に立つことを願っています.
背景
我々は開発過程でjava開発が大部分であり,テキスト処理過程では主にスクリプト開発が行われている.JAva開発の特徴は、TDDL、METAQなどのドッキングを早く行うことができることです.スクリプト開発の特徴は,バッチ処理を行う際に非常に便利であることである.先日、キャプチャしたデータをパッケージ化し、nodejsの下でphantomjsベースのcasperjs爬虫類などのスクリプトでキャプチャする必要があるシーンに遭遇しました.
解決策
最初の問題:javaがキャプチャし、結果をパッケージ化します.直接的な方法はJAvaは各種メッセージを受信する(db,metaqなど)を使用し、jstormクラスタを介してスケジューリングおよびキャプチャを行います.最後にキャプチャの結果を1つのファイルに保存し、shellを呼び出してパッケージングし、返信します.java呼び出しodpsを直接ファイルに保存しない理由という質問があるかもしれません.答えは、私たちのクラスタはhzクラスタではなくodpsを直接アップロードする速度に問題があるので、先にパッケージングします適当です.(ここはデザインにこだわらず、本題に戻ります)
JAvaがshellを呼び出す方法
プロセスビルダーによるスケジューリング
この方法は直感的で、パラメータの設定も便利です.例えば、私が実践しているコード(ビジネスコードの一部を隠しています):
ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, param1,
param2, param3);
pb.directory(new File(SHELL_FILE_DIR));
int runningStatus = 0;
String s = null;
try {
Process p = pb.start();
try {
runningStatus = p.waitFor();
} catch (InterruptedException e) {
}
} catch (IOException e) {
}
if (runningStatus != 0) {
}
return;
ここではいくつかのパラメータを説明する必要があります:RUNNING_SHELL_FILE:実行するスクリプトSHELL_FILE_DIR:実行するスクリプトがあるディレクトリ;もちろん、実行するスクリプトをフルパスに書くこともできます.runningStatus:運転状態、0は正常です.詳細はjavaドキュメントを参照してください.param 1,param 2,param 3:RUNNINGでできるSHELL_FILEスクリプトで直接1,2,$3でそれぞれ得られるパラメータ.
直接システムRuntimeでshellを実行
この方法は暴力的で、よく使われています.コードは以下の通りです.
p = Runtime.getRuntime().exec(SHELL_FILE_DIR + RUNNING_SHELL_FILE + " "+param1+" "+param2+" "+param3);
p.waitFor();
Runtime方式ではbuilderほど便利ではないことが分かった.特にパラメータの面では、execが文字列全体をshellとして実行するため、自分でスペースを付けなければならない.
存在する可能性のある問題と解決方法
上を通ることであなたのニーズを満たすことができると思ったら、壁にぶつかるかもしれません.次のような状況に遭遇します.
実行権限なし
この状況で私たちのチームの朱東方は出会って、DTSの移行をする過程で、パッケージの中のshellスクリプトを実行して、解凍してから、実行できないことに気づきました.では、上記の方法で授権しましょう.
ProcessBuilder builder = new ProcessBuilder("/bin/chmod", "755", tempFile.getPath());
Process process = builder.start();
int rc = process.waitFor();
JAvaはshellの戻りを待つ
この問題はもっとよく出会うだろう.なぜなら、shellスクリプトにechoまたはprint出力があり、バッファが切れてしまったからです.このような状況を避けるために、バッファを必ず読んで、shellの具体的な運転状態をlogすることができるというメリットがあります.例えば、上記の例では、
ProcessBuilder pb = new ProcessBuilder("./" + RUNNING_SHELL_FILE, keyword.trim(),
taskId.toString(), fileName);
pb.directory(new File(CASPERJS_FILE_DIR));
int runningStatus = 0;
String s = null;
try {
Process p = pb.start();
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((s = stdInput.readLine()) != null) {
LOG.error(s);
}
while ((s = stdError.readLine()) != null) {
LOG.error(s);
}
try {
runningStatus = p.waitFor();
} catch (InterruptedException e) {
}
start()の後、waitFor()の前にバッファを読み出してlogを打つと、shellが予想通りに実行されていない理由がわかります.これはshellから出力された結果を読むことができ、javaコードのさらなる操作を容易にするというメリットがあります.
手作業で実行できるコマンドなのに、javaが呼び出したshellのコマンドが実行できないという問題に直面するかもしれません.エラー:コマンドは存在しません.
例えばcasperjsを使っているとき、shellを手動で実行するのは実行できるのにjava呼び出しのとき、いつもエラーが発生していることに気づきました.バッファを読み込むことでエラーログが見つかります.インストールしたcasperjsのbinをpathに追加しても十分ではないことに気づきました(/etc/profile、各種bashrc)
export NODE_HOME="/home/admin/node"
export CASPERJS_HOME="/home/admin/casperjs"
export PHANTOMJS_HOME="/home/admin/phantomjs"
export PATH=$PATH:$JAVA_HOME/bin:/root/bin:$NODE_HOME/bin:$CASPERJS_HOME/bin:$PHANTOMJS_HOME/bin
Javaがshellを呼び出すとき、デフォルトではシステムの/bin/下の命令が使われていたからです.特にroot権限で実行しているとき.この時、あなたは/binの下にソフトチェーンを追加します.私の上記の例では、/binの下にソフトチェーンを追加します.
ln -s /home/admin/casperjs/bin/casperjs casperjs;
ln -s /home/admin/node/bin/node node;
ln -s /home/admin/phantomjs/bin/phantomjs phantomjs;
これで、問題は解決できます.
Java呼び出しshellでパッケージ化されている場合は、パスの問題に注意してください.
shellのtarの圧縮と解凍は直接書くことができません.
tar -zcf/home/admin/data/result.tar.gz/home/admin/data/result
直接エラーを報告します.tarの圧縮源はパスの下に行かなければならないので、書くことができます.
tar -zcf/home/admin/data/result.tar.gz -C/home/admin/data/result
もし私のshellがjarパッケージにいたらどうしますか?
答えは:解凍します.上記の指示に従って操作します.(1)経路を見つける
String jarPath = findClassJarPath(ClassLoaderUtil.class);
JarFile topLevelJarFile = null;
try {
topLevelJarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = topLevelJarFile.entries();
while (entries.hasMoreElements()) {
JarEntry entry = entries.nextElement();
if (!entry.isDirectory() && entry.getName().endsWith(".sh")) {
shell
}
}
ファイルの処理方法は簡単で、直接touchの一時ファイルをtouchして、それからデータストリームを書き込んで、コード:
FileUtils.touch(tempjline);
tempjline.deleteOnExit();
FileOutputStream fos = new FileOutputStream(tempjline);
IOUtils.copy(ClassLoaderUtil.class.getResourceAsStream(r), fos);
fos.close();
これがあれば、踏み込みを減らし、javaとスクリプトのインタラクションを大胆に使うことができると信じています.JAvaはshellを呼び出すことができるので、shellは他のものを呼び出すのが便利です.バッファに過度に依存してスレッド間の通信を行わないでください.原因は自分で勉強しましょう.
これが皆さんの役に立つことを願っています.