首页

文章

怎么通过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();
八月中国最凉快的地方 八月份哪里最凉快,去哪旅游好?美丽的地方 乱字同韵字是什么意思 华硕笔记本电脑触摸板怎么开笔记本电脑触摸板怎么开启和关闭_百度知 ... 陕西职务侵占案立案准则 结婚后我的恋情维系了十年,怎么做到的? 玉米仁子饭产自哪里 中国期货交易所的交易品种有哪些? 历史要怎么读,有啥诀窍 高中历史诀窍 年终会活动策划方案 深度解析:第一财经回放,探索财经新风向 逆水寒手游庄园怎么邀请好友同住 逆水寒手游 逆水寒不同区可以一起组队吗? 逆水寒手游 逆水寒怎么进入好友世界? 逆水寒手游 逆水寒怎么去别人的庄园? 使用puppeteer实现将htmll转成pdf 内卷时代下的前端技术-使用JavaScript在浏览器中生成PDF文档 【译】将HTML转为PDF的几种实现方案 变形金刚08动画怎么样 变形金刚08动画的问题 变形金刚08动画日语版剧情介绍 高分!换显卡nvidia控制面板被我卸了,重新安装显卡驱动后没了nvidia控... 我的nvidia控制面板被卸载了 怎么找回啊 卸载后 这个画面看着很奇怪_百 ... 李卓彬工作简历 林少明工作简历 广东工业职业技术学院怎么样 郑德涛任职简历 唐新桂个人简历 土地入股的定义 ups快递客服电话24小时 贷款记录在征信保留几年? 安徽徽商城有限公司公司简介 安徽省徽商集团新能源股份有限公司基本情况 安徽省徽商集团有限公司经营理念 2019哈尔滨煤气费怎么有税? 快手删除的作品如何恢复 体育理念体育理念 有关体育的格言和理念 什么是体育理念 万里挑一算彩礼还是见面礼 绿萝扦插多少天后发芽 绿萝扦插多久发芽 扦插绿萝多久发芽 炖牛排骨的做法和配料 网络诈骗定罪标准揭秘 “流水不争先”是什么意思? mc中钻石装备怎么做 为什么我的MC里的钻石块是这样的?我想要那种。是不是版本的问题?如果是... 带“偷儿”的诗句 新手学电脑先学什么 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前置摄像头的像素是多少?像素好吗?
声明声明:本网页内容为用户发布,旨在传播知识,不代表本网认同其观点,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。E-MAIL:11247931@qq.com