问答文章1 问答文章501 问答文章1001 问答文章1501 问答文章2001 问答文章2501 问答文章3001 问答文章3501 问答文章4001 问答文章4501 问答文章5001 问答文章5501 问答文章6001 问答文章6501 问答文章7001 问答文章7501 问答文章8001 问答文章8501 问答文章9001 问答文章9501

怎么通过java去调用并执行shell脚本以及问题总结

发布网友 发布时间:2022-04-19 22:43

我来回答

2个回答

懂视网 时间:2022-04-08 09:04

        本实例为用Java执行shell脚本启动或关闭远程Mysql数据库,需求原因:游戏服务器合服后,为了节省内存消耗,需要关闭合服后的服务器不必要的数据库(一台服务器主机存在多个MySql数据库),以提高服务器性能,但有时需要查询历史游戏玩家信息,又需要开启数据库,为了节省运维人员的人力和时间,游戏后台就提供非运维人员都可操作开关数据库的操作。

功能实现步骤:

第一:服务器后台提供参数,发送异步请求,请求方法如下

<script type="text/javascript">
	function shutdownDatabase(param,operate){
		var arr=param.replace(/s+/g," ").split(" ");
		if(arr.length!=3){
			alert("数据库脚本参数个数不正确,请编辑好再操作!");
			return false;
		}
		
		param = param + " " + operate;
		$.ajax({
			url:'XXXAction!databaseSwitch.action',
			type:"post",
			data:{"param":param},
			dataType:"json",
			async:true,
			success:function(data){
				$("#progressImgage").hide();//进度条隐藏
				$("#maskOfProgressImage").hide();//蒙版隐藏
				alert("执行结果:"+data.result);
			},
			error:function(data){
				$("#progressImgage").hide();
				$("#maskOfProgressImage").hide();
				alert("执行结果:"+data.result);
			},
			beforeSend:function(xhr){
				$("#progressImgage").show().css({
	  "position": "fixed",
	  "top": "50%",
	  "left": "50%",
	  "margin-top": function () { return -1 * $("#progressImgage").height() / 2; },
	  "margin-left": function () { return -1 * $("#progressImgage").width() / 2; }
	  });
				$("#maskOfProgressImage").show().css("opacity", "0.1");
			}
		}); 
	}
</script>
其中param为shell脚本参数,根据具体业务而不同。
请求等待提示代码

<img id="progressImgage" class="progress hide" alt="数据库操作中" src="./images/loading.jpg" width="250" height="200"/>
<div id="maskOfProgressImage" class="mask hide"></div>
<style type="text/css">
 .hide{display:none }
 .progress{z-index: 2000}
 .mask{position: fixed;top: 0;right: 0;bottom: 0;left: 0; z-index: 1000; background-color: #000000}
</style> 

第二,后台java代码,发送异步请求到后台java方法,java方法操作执行shell脚本,执行代码如下

/**
	 * 服务器数据库开关
	 */
	public void databaseSwitch() {
		logger.info("服务器数据库开关 start");
		PrintWriter out = null;
		try {
		 ActionContext context = ActionContext.getContext(); 
		 HttpServletRequest request = (HttpServletRequest)context.get(ServletActionContext.HTTP_REQUEST); 
		 HttpServletResponse response = (HttpServletResponse)context.get(ServletActionContext.HTTP_RESPONSE); 
		 request.setCharacterEncoding("UTF-8");
		 response.setCharacterEncoding("UTF-8"); 
		 out= getServletResponse().getWriter();
		 
		 
		 String param= request.getParameter("param"); 
		 if (null == param || "".equals(param)) {
		 	out.write("{"result":"脚本参数不能为空"+""}");
		 	return;
			}
		 String shellStr = "sh /data0/mysql_actions.sh "+param;
			logger.info("执行脚本:"+shellStr);
			List<String> result = new ArrayList<String>();
			result = execShell(shellStr);
			
			out.write("{"result":""+result.toString()+""}");
			logger.info("执行结果:"+result);
		} catch (UnsupportedEncodingException e1) {
			out.write("{"result":"操作数据库时不支持字符编码出错!"}");
			logger.error("execShell方法异常"+e1);
			e1.printStackTrace();
		} catch (Exception e) {
			out.write("{"result":"操作数据库出错!"}");
			logger.error("execShell方法异常");
			e.printStackTrace();
		}
		logger.info("服务器数据库开关 end");
	}
 
 /** 
 * 运行shell 
 * @param shStr 
 * 需要执行的shell 
 * @return 
 * @throws IOException 
 */ 
 public static List<String> execShell(String shStr) throws Exception { 
 List<String> strList = new ArrayList<String>(); 
 Process process; 
 process = Runtime.getRuntime().exec(new String[]{"/bin/sh","-c",shStr},null,null); ////执行shell命令 
 InputStreamReader ir = new InputStreamReader(process.getInputStream()); 
 LineNumberReader input = new LineNumberReader(ir); //脚本输出
 String line; 
 int extValue = process.waitFor(); //执行结果
 strList.add("extValue="+extValue);
 while ((line = input.readLine()) != null){ 
  strList.add(line); 
 } 
 process.destroy();//杀掉进程
 return strList; 
 } 

执行脚本的主要方法为:execShell(String shStr),其中shStr参数为要执行的shell脚本,本实例的脚本写成文件直接调用,这样可读性,可移植性强。


第三,关闭远程服务器数据库

主要是在shell脚本中通过ssh,连接远程服务器,通过脚本判断数据库是否开启并执行开关操作,最后返回结果

关键代码为:

ssh="ssh -c arcfour128 -o MACs=umac-64@openssh.com -o StrictHostKeyChecking=no -o GSSAPIAuthentication=no -p 22"

$ssh $1 "ps -ef |grep mysql |grep `cat /data/${3}/mysql/port`" &> /dev/null
 if [ $? -eq 0 ];then
 echo "主库目前已经处于运行状态"
 else
 $ssh $1 "sh /data0/${3}/mysql/start-mysql.sh &> /dev/null"
 if [ $? -eq 0 ];then
  echo "成功开启主库"
 else
  echo "开启主库失败"
 fi
 fi

其中$1参数为远程服务器ip

详细代码就不贴出来了,详解请私信我。这个功能做出来是非常耗时的,但做出来后,别人就很省时了,含金量比较大。

如果脚本不熟,需要运维人员提供。一个人写需要懂的技术很多,深度很深。


问题1:我执行脚本期间遇到一个问题就是没有在root用户下执行,导致失败,执行脚本需在管理员权限下执行才能成功。

问题2:后台需要远程服务器的无密码登录权限,通过key设置,ssh还要设置为“ForwardAgent yes”。

一切就绪后台即可执行,祝读者好运!






版权声明:本文为博主原创文章,未经博主允许不得转载。

Java执行shell脚本关闭远程数据库

标签:shell   java执行shell脚本   shell关闭数据库   服务器   

热心网友 时间:2022-04-08 06:12

对于第一个问题:java抓取,并且把结果打包。那么比较直接的做法就是,java接收各种消息(db,metaq等等),然后借助于jstorm集群进行调度和抓取。
最后把抓取的结果保存到一个文件中,并且通过调用shell打包, 回传。 也许有同学会问,
为什么不直接把java调用odps直接保存文件,答案是,我们的集群不是hz集群,直接上传odps速度很有问题,因此先打包比较合适。(这里不纠结设计了,我们回到正题)

java调用shell的方法

通过ProcessBuilder进行调度

这种方法比较直观,而且参数的设置也比较方便, 比如我在实践中的代码(我隐藏了部分业务代码):

ProcessBuilderpb = 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文档。

param1, param2, param3:可以在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脚本, 解压出来了之后,发现执行不了。 那么就按照上面的方法授权吧

java进行一直等待shell返回

这个问题估计更加经常遇到。 原因是, shell脚本中有echo或者print输出, 导致缓冲区被用完了! 为了避免这种情况, 一定要把缓冲区读一下, 好处就是,可以对shell的具体运行状态进行log出来。 比如上面我的例子中我会变成:

ProcessBuilderpb = 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();
BufferedReaderstdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReaderstdError = 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中)还不够。 比如:

exportNODE_HOME="/home/admin/node"
exportCASPERJS_HOME="/home/admin/casperjs"
exportPHANTOMJS_HOME="/home/admin/phantomjs"
exportPATH=$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/casperjscasperjs;
ln -s /home/admin/node/bin/nodenode;
ln -s /home/admin/phantomjs/bin/phantomjsphantomjs;

这样,问题就可以解决了。

如果是通过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);
JarFiletopLevelJarFile = null;
try {
topLevelJarFile = new JarFile(jarPath);
Enumeration<JarEntry> entries = topLevelJarFile.entries();
while (entries.hasMoreElements()) {
JarEntryentry = entries.nextElement();
if (!entry.isDirectory() entry.getName().endsWith(".sh")) {
对你的shell文件进行处理
}
}

对文件处理的方法就简单了,直接touch一个临时文件,然后把数据流写入,代码:

FileUtils.touch(tempjline);
tempjline.deleteOnExit();
FileOutputStreamfos = new FileOutputStream(tempjline);
IOUtils.copy(ClassLoaderUtil.class.getResourceAsStream(r), fos);
fos.close();
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com
色彩中蓝色、红色、黄色除了加白色以外还有什么办法提高明度 色彩不敢调亮怎么办? 色彩如何提亮 男朋友惹我生气了 事后一点悔意也没 都在气头上 我不小心打男朋友了 他... 解签:为人处事莫亏心,暗室之中有灵应,一时得意反成失,半点悔改胜似金... 我手机之前一段时间由于欠费停机了 前几天我充了50元进去怎么还是... 撕名牌可以用哪些超能力技能? 为什么5孔插座带开关,安装好之后,灯有电,插座不通电? 怎么把照片内存变小 图片内存变小的方法 百度地图导航如何连接车蓝牙 百度地图导航连接车蓝牙方法 新手学电脑先学什么 JAVA中如何执行DOS命令 新手使用笔记本电脑有什么技巧? java怎么杀掉java进程 java中如何执行命令行语句 笔记本电脑初学者怎样使用? 怎么在java代码中写入DOS命令 运行在linux下的java程序,调用了命令行,如何让java... 电脑新手怎么学习使用电脑? 如何用Java关闭一个进程 如何在Java中执行其它程序 java中如何执行一个应用程序 java process.waitfor=3是什么意思 Java Process.waitFor()这个方法是做什么用的?是不... 怎么样找到自己电脑在局域网中的IP ? win7系统查看同一个局域网内电脑IP的方法 要怎么系统还原? 西门子洗衣服,快洗快还是混合洗的快 西门子洗衣机洗洗就停了怎么回事? ETL的空投活动怎么参与? 请问各位我是个新手,刚买了新的笔记本电脑,应该... JAVA 如何停止一个运行着的指定线程? 如何用java执行命令行 初学者怎样学电脑比较好? 如何在java中执行shell脚本 java中如何清屏? 电脑初学者怎样使用电脑? Java中有什么代码是可以清屏啊?? Process.waitFor()、Runtime.getRuntime().exec方... java accept阻塞 用Java代码怎么在浏览器中显示一个网页 新手如何买电脑? 新手上电脑怎么操作把资料考进u盘 手机的前置30W像素摄像头拍出的照片能不能看清楚? 为什么iphone4s前置摄像头只有30万像素? 手机摄像头像素:30万像素算清晰吗? 前置摄像头30万像素,后置500万,总的来说,摄像功... iphone4s前置摄像头和后置摄像头,像素各是多少? 华为前置摄像头30万像素怎么样? iphone4s前置摄像头的像素是多少?像素好吗?