使用JAVA调用SHELL命令的一个严重问题
项目需要使用JAVA调用SHELL命令,在测试环境中一切完好,到了生产环境,行不通了!!原因是一串命令本来是要到后台去执行的。结果到了生产上没有后台执行!导致运行时间很长,因为如果该SHELL脚本没有后台执行的话,运行时间就是该SHELL脚本运行的时间!
需要后台执行的命令是这样的:
su - oracle -c "nohup /home/temp/shell.sh > /dev/null&";
生产上的环境是AIX5.3!在测试环境中,也有AIX5.3,但是这个问题就是重现不了,一到生产上,上面的命令就无法后台执行,老是要执行很长时间,搞得我很郁闷!大家帮帮忙啊
JAVA代码如下:
/**
* <pre>
* Title: LinuxCodeDispenser.java
* Project: RPT2.6
* Type: com.sf.module.jobmgmt.biz.LinuxCodeDispenser
* Author: 255507
* Create: 2011-12-9 上午10:16:06
* Copyright: Copyright (c) 2011
* Company:
* <pre>
*/
package com.sf.module.jobmgmt.dispenser;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.text.MessageFormat;
import org.apache.commons.io.IOUtils;
import org.apache.log4j.Logger;
import com.sf.module.jobmgmt.domain.JobExecuteParameter;
import com.sf.module.jobmgmt.onlineconfig.OnlineConfigHandler;
import com.sf.module.jobmgmt.util.TaskTransUtils;
/**
* <pre>
* 代码分发器的Unix实现,功能是在Unix服务器之间发送并执行相应的代码
* 条件是两台Unix服务器之间建立了信任关系
* </pre>
* @author 255507
* @version 1.0, 2011-12-9
*/
public class UnixCodeDispenser implements ICodeDispenser
{
/**
* scp /home/leoly/cmd.sh root@10.0.14.111:/home/oracle/
* 上传文件到UNIX平台的命令
*/
private final String SCP_COMMAND = "scp {0} {1}@{2}:{3}";
/**
* ssh 10.0.14.111 登录到UNIX平台的命令 不提供密码,需要两台机子互信
*/
private final String LOGIN_COMMAND = "ssh {0}";
/**
* chmod +x /home/oracle/2_100002_100000.sh; 增加UNIX平台文件可执行权限的命令
*/
private final String CHMOD_COMMAND = "chmod +x {0};";
/**
* su - oracle -c
* "nohup /home/oracle/2_100002_100000.sh > /dev/null&";
* 执行ORACLE的SQL文件的命令
*/
private final String ORACLE_COMMAND = "su - oracle -c \"nohup {0} > {1}&\";";
/**
* exit; 登出命令
*/
private final String LOGOUT_COMMAND = "exit;";
private Logger log = Logger.getLogger(UnixCodeDispenser.class);
/*
* (非 Javadoc)
* @see
* com.sf.module.jobmgmt.biz.ICodeDispenser#sendAndExecCode(java
* .lang.String)
*/
public void sendAndExecCode(JobExecuteParameter param)
{
ResultService resultService = ExecuteResultService.getInstance();
long start = System.currentTimeMillis();
// 先上传文件到服务器,然后登录服务器,增加文件的执行权限,再执行该文件
if (uploadFile(param, resultService) == EXECUTE_SUCCESS)
{
OnlineConfigHandler cfgHandler = OnlineConfigHandler.getInstance();
String mainCmd = MessageFormat.format(cfgHandler.getString("LOGIN_COMMAND", LOGIN_COMMAND),
param.getRemotIp());
String localPath = param.getLocalFilePath();
if (localPath.lastIndexOf(File.separator) > 0)
{
String fileName = localPath.substring(localPath.lastIndexOf(File.separator) + 1);
String remotFilepath = param.getRemotFilePath() + File.separator + fileName;
String chmodCmd = MessageFormat.format(cfgHandler.getString("CHMOD_COMMAND", CHMOD_COMMAND),
remotFilepath);
String oracleCmd = MessageFormat.format(cfgHandler.getString("ORACLE_COMMAND", ORACLE_COMMAND),
remotFilepath, "/dev/null");
String[] cmds = { chmodCmd, oracleCmd, cfgHandler.getString("LOGOUT_COMMAND", LOGOUT_COMMAND) };
try
{
execCmds(mainCmd, cmds, resultService, param);
log.info("################Execute Command Use Time: " + (System.currentTimeMillis() - start) / 1000 + "s");
}
catch (IOException e)
{
resultService.doAfterExecute(param, EXECUTE_FAILTURE, e.getMessage());
log.error(null, e);
}
catch (InterruptedException e)
{
resultService.doAfterExecute(param, EXECUTE_FAILTURE, e.getMessage());
log.error(null, e);
}
}
else
{
String errorMsg = "Locale file path error:" + localPath;
log.error(errorMsg);
resultService.doAfterExecute(param, EXECUTE_FAILTURE, errorMsg);
}
log.info("################All Execute Command Use Time: " + (System.currentTimeMillis() - start) / 1000 + "s");
}
}
/**
* <pre>
* 执行上传文件的命令
* </pre>
* @param filePath 文件路径
* @return
*/
private int uploadFile(JobExecuteParameter param, ResultService resultService)
{
Runtime runtime = Runtime.getRuntime();
BufferedReader stdReader = null;
BufferedReader errReader = null;
int cmdResult = EXECUTE_FAILTURE;
try
{
OnlineConfigHandler cfgHandler = OnlineConfigHandler.getInstance();
String scpCmdStr = cfgHandler.getString("SCP_COMMAND", SCP_COMMAND);
String scpCmd = MessageFormat.format(scpCmdStr, param.getLocalFilePath(), param.getRemotUser(),
param.getRemotIp(), param.getRemotFilePath());
log.info("*********************Execute command: " + scpCmd);
Process process = runtime.exec(scpCmd);
cmdResult = process.waitFor();
if (cmdResult != EXECUTE_SUCCESS)
{
InputStream errin = process.getErrorStream();
String errInfo = IOUtils.toString(errin);
errInfo = "Execute command: [" + scpCmd + "] error! error infomation: " + errInfo;
log.error(errInfo);
resultService.doAfterExecute(param, EXECUTE_FAILTURE, errInfo);
}
}
catch (IOException e)
{
log.error(null, e);
resultService.doAfterExecute(param, EXECUTE_FAILTURE, e.getMessage());
}
catch (InterruptedException e)
{
log.error(null, e);
resultService.doAfterExecute(param, EXECUTE_FAILTURE, e.getMessage());
}
finally
{
IOUtils.closeQuietly(errReader);
IOUtils.closeQuietly(stdReader);
}
return cmdResult;
}
/**
* <pre>
* 执行一系列命令
* </pre>
* @param cmds 命令系列
* @throws IOException
* @throws InterruptedException
*/
public void execCmds(String mainCmd, String[] cmds, ResultService resultService, JobExecuteParameter param)
throws IOException, InterruptedException
{
Runtime runtime = Runtime.getRuntime();
Process process = runtime.exec(mainCmd);
OutputStream os = process.getOutputStream();
PrintWriter pw = new PrintWriter(os);
for (String cmd : cmds)
{
pw.write(cmd);
pw.flush();
}
pw.close();
int reslt = process.waitFor();
// 执行成功的操作:修改任务执行队列表的状态
if (reslt == EXECUTE_SUCCESS)
{
resultService.doAfterExecute(param, EXECUTE_SUCCESS, null);
}
else
{
InputStream errin = process.getErrorStream();
String errInfo = IOUtils.toString(errin);
errInfo = "Execute command: [" + TaskTransUtils.toString(cmds, ", ") + "] error! error infomation: "
+ errInfo;
log.error(errInfo);
IOUtils.closeQuietly(errin);
resultService.doAfterExecute(param, EXECUTE_FAILTURE, errInfo);
}
}
}
请大家为我分析原因,谢谢各位了!
--------------------编程问答-------------------- 那在生產環境相關的錯誤日誌有沒有? --------------------编程问答--------------------
没有,这命令本来是后台执行的,现在在生产机子上根本没有后台执行。
错误就是执行时间很长,但不报错 --------------------编程问答--------------------
這裡也都有log.error(errInfo);記錄啊
能知道是在哪句執行時停滯了嗎? --------------------编程问答-------------------- --------------------编程问答--------------------
现在可以证明,就是在执行
su - oracle -c "nohup /home/oracle/cmd.sh > /home/oracle/cmd.sh.log &"
这句的时候没有转后台执行,导致这句命令需要等待cmd.sh这个文件执行完成后才返回结果
大家帮忙分析下 --------------------编程问答--------------------
命令本身并没有报错,只是没有转后台而已,所以logger.error(errinfo)获取不到报错信息 --------------------编程问答-------------------- 那重點研究這一句及其上下文
su - oracle -c "nohup /home/oracle/cmd.sh > /home/oracle/cmd.sh.log &"
你說的命令本身沒錯,那在測試環境執行成功,而產品環境執行失敗,這兩者有什麽區別呢
不通過java,直接在產品環境的linux下能運行這句嗎? --------------------编程问答--------------------
直接运行可以,通过JAVA调用就不行 --------------------编程问答--------------------
那研究下測試環境爲什麽通過java調就可以呢,測試環境多了點什麽還是少了點什麽 --------------------编程问答-------------------- su - oracle -c "nohup /home/oracle/cmd.sh > /home/oracle/cmd.sh.log 2>&1 &
把错误流和标准输出流合并输入到日志文件中,这样就行了
这易做图问题搞了我半个月!! --------------------编程问答--------------------
這樣就能執行了,還是因此看出了問題在哪裡? --------------------编程问答--------------------
不明白啊,可以告诉我吗?我对命令是一知半解的 --------------------编程问答-------------------- 你把你的shell字符串转成字符串数组传到java调用程序中试试
补充:Java , Java EE