场景:
在linux服务器上有这样一个脚本文件
/home/xxxx/my_script/run_dev_job_etl_usermonitor_task_list.sh
需求:
在java代码中直接调用此sh脚本文件
方法:
定义一个接口
com.xxx.service.ShellService
java调用本地shell脚本
@Override
public int exec(String[] cmds) throws Exception {
String s;
Process p = Runtime.getRuntime().exec(cmds);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
while ((s = stdInput.readLine()) != null) {
logger.info(s);
}
while ((s = stdError.readLine()) != null) {
logger.error(s);
}
try {
int result = p.waitFor();
logger.info("执行shell脚本结果:{}",result);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 执行成功返回0
return 0;
}
获取响应字符流方式,阻塞在这里
process = Runtime.getRuntime().exec("tail -f /var/log/syslog");
inputStream = process.getInputStream();
// 一定要启动新的线程,防止InputStream阻塞处理WebSocket的线程
TailLogThread thread = new TailLogThread(inputStream, session);
thread.start();
java通过ssh方式调用远程shell脚本
引入依赖
compile group: 'org.jvnet.hudson', name: 'ganymed-ssh2', version: 'build210-hudson-1'
@Override
public int exec(String[] cmds) throws Exception {
InputStream stdOut = null;
InputStream stdErr = null;
String outStr;
String outErr;
int ret;
try {
if (login()) {
// Open a new {@link Session} on this connection
Session session = connection.openSession();
// Execute a command on the remote machine.
session.execCommand(cmds[0]);
stdOut = new StreamGobbler(session.getStdout());
outStr = processStream(stdOut, charset);
stdErr = new StreamGobbler(session.getStderr());
outErr = processStream(stdErr, charset);
if (StringUtils.isNotEmpty(outStr)) {
logger.info("outStr=" + outStr);
}
if (StringUtils.isNotEmpty(outErr)) {
logger.info("outErr=" + outErr);
// 获取hive执行时长
String hiveExecutionTime = StringUtils.substringBetween(outErr, "Time taken:", " seconds");
logger.info("hiveExecutionTime:{}", hiveExecutionTime);
}
session.waitForCondition(ChannelCondition.EXIT_STATUS, TIME_OUT);
// 一般情况下shell脚本正常执行完毕,getExitStatus方法返回0
ret = session.getExitStatus();
return ret;
} else {
// 自定义异常类 实现略
throw new Exception("登录远程机器失败" + ip);
}
} finally {
if (connection != null) {
connection.close();
}
IOUtils.closeQuietly(stdOut);
IOUtils.closeQuietly(stdErr);
}
}
private boolean login() throws IOException {
connection = new Connection(ip);
connection.connect();
return connection.authenticateWithPassword(username, password);
}
获取响应字符流方式,阻塞在这里
ch.ethz.ssh2.Session sshSession = connection.openSession();
sshSession.execCommand("tail -0f /var/log/prometheus.log");
InputStream inputStream = new StreamGobbler(sshSession.getStdout());
安全注意
需要注意:操作系统命令注入
Java 语言提供了类似 Runtime.exec(…) 的 API,可以用来执行特定命令,假设我们构建了一个应用,以输入文本作为参数,执行下面的命令:
ls –la input_file_name
但是如果用户输入是
input_file_name; rm –rf /*
这就有可能出现问题了。当然,这只是个举例,Java 标准类库本身进行了非常多的改进,所以类似这种编程错误,未必可以真的完成攻击,但其反映的一类场景是真实存在的。