文档章节

使用Java调用shell脚本时遇到的问题

Zero零_度
 Zero零_度
发布于 2017/05/05 21:21
字数 1313
阅读 80
收藏 1

使用Java调用shell脚本时遇到的问题

最近Jackie在搞一个新项目,为了快速完成开发,需要在Java代码里使用shell脚本或者命令,便于快速完成业务需要的功能。Java SDK中关于启动进程执行外部shell命令的API很简单,很直接,所以Jackie直接参考样例就开搞了,原本以为很顺利,结果遇到了一些问题,花费了不少精力才解决。 出于各方面原因,我厂的编程规范不推荐在Java代码里调用shell脚本或者命令,另外Java的开源库非常丰富,所以日常工作中几乎没有场景需要在代码中直接调用shell脚本的需求。这直接导致Jackie欠缺相关使用经验,在简单的问题上花费了相当的时间。 本文记录Jackie在相关API使用过程中遇到的问题。

使用Java调用shell脚本的方法

方法一

这个方法比较直接,简单,适用于一般的场景,比如Jackie当前在做的项目。

Process process = Runtime.getRuntime().exec(shellCommand);  

方法二

这个方法相对要复杂一些,但对于需要向脚本传递参数的场景,会非常方便。但Jackie在项目里没有遇到类似的场景,所以没有使用这种方法。

ProcessBuilder ProcessBuilder = new ProcessBuilder(shellCommand, param1, param2);

注意事项

在Java代码中调用shell脚本或者命令时,有一些小细节要注意,否则等程序运行起来,就会发现各种小问题。

  1. shell脚本的格式需要为unix格式,如果不是的话,需要使用dos2unix或者tr等命令对脚本文件的格式做处理,删除多余的\r。 命令的参考样例如下
    tr -d "\r" < winfile.sh > unixfile.sh
    dos2unix winfile.sh
    
  2. shell脚本或者命令需要有可执行权限,这个很好理解,不解释。
    chmod +x command.sh
    
  3. shell脚本或者命令的路径。为了保证可靠性,比较省事的方法是在代码中使用脚本或者命令的全路径。在脚本里执行其它脚本或者命令时,也尽可能使用脚本或者命令的全路径,省掉准备PATH环境变量的工作。当然了,这会引入一个问题,即对脚本全路径的依赖,假如全路径是非标准路径,同时不同的设备上路径存在差异,那么Jackie推荐的做法是使用模板技术,比如Freemarker,将变化的部分抽取出来,作为变量插入到脚本里,每次执行脚本时,使用外部的变量来替换脚本中的路径,解决上述问题。但最有效、最省事的办法还是对路径进行标准化,保证脚本、命令的位置在所有设备上一致,这可以有效提高开发、运维的工作效率。
  4. 如需等待shell命令运行结束,获取运行结果,可以使用 ProcesswaitFor方法,样例如下:
    Process process = Runtime.getRuntime().exec(shellCommand);  
    int code = process.waitFor();// waitFor方法的返回值,表示了shell命令或者脚本的返回值,方便我们的代码做进一步的处理。
    
  5. 启动shell进程后,发现进程长时间运行无法结束,同时失去响应。这个问题的原因是shell脚本或者命令在运行的过程中会向标准输出或者标准错误输出写出数据,但JVM又没有去读,导致缓冲区满,进而导致进程阻塞。这个问题的解决的方法比较简单,既然问题是缓冲区满之后没有及时清理,那么只要在Java代码里去读一下数据,保证缓冲区不会满即可。可参考如下实现。
    Process process = Runtime.getRuntime().exec(shellCommand);  
    BufferedReader stdout = new BufferedReader(new InputStreamReader(process.getInputStream())); 
    BufferedReader stderr = new BufferedReader(new InputStreamReader(process.getErrorStream())); 
    String line;
    while ((line = stdout.readLine()) != null)
    {
        // do something, logging
    }
    while ((line = stderr.readLine()) != null)
    {
        // do something, logging
    }
    process.waitFor(); // 等待程序运行结束
    
  6. shell命令中如果包含了管道或者重定向的操作,在SSH终端里可以正常运行,但使用Java代码运行时会报一些奇怪的错误。这个问题让Jackie纠结了半个下午,排查了好久,后来在网上搜索了一些文章,终于确认是使用方法的问题。 问题代码
    Process process = Runtime.getRuntime().exec("tr -d '\r' < run.sh.template > run.sh");  
    
    正确的代码
    Process process = Runtime.getRuntime().exec("/bin/sh", "-c", "tr -d '\r' < run.sh.template > run.sh");  
    
    差别在于带有管道或者重定向操作的命令需要作为参数传递给命令/bin/sh方能正常执行,否则就会报各种莫明其妙的错误,让人百思不得其解。
  7. 其它问题。等遇到了再来总结吧。

参考资料

网上关于Java代码中调用shell脚本或者命令的文章非常多,Jackie只是摘录了一部分。

本文转载自:http://blog.csdn.net/jackie_xiaonan/article/details/60891202

下一篇: nohup方式执行
Zero零_度
粉丝 69
博文 1258
码字总数 257684
作品 0
程序员
私信 提问
java调用shell脚本返回值问题,求助!!!!!!!!

刚搞shell脚本这块,现在遇到个问题,望知道的大神帮忙解答下,感谢啊! 问题是这样的: java后台调用shell脚本,脚本作用是解析数据库用户名(dbuserName)和数据库密码(dbuserPwd),我打算让d...

踩单车的老爷爷
2016/02/23
359
3
关于 Java Scripting API 您不知道的 5 件事

现在,许多 Java 开发人员都喜欢在 Java 平台中使用脚本语言,但是使用编译到 Java 字节码中的动态语言有时是不可行的。在某些情况中,直接编写一个 Java 应用程序的脚本 部分 或者在一个脚本...

红薯
2010/09/12
495
2
请教linux下java调用shell实现自动重启的问题

应用场景是实现java程序的远程升级,java程序运行在linux下,是一个后台一直运行的服务。 大概流程如下: 1.java程序判断接收到升级包,调用linux shell脚本来进行升级 Runtime.getRuntime()...

风之刀
2017/06/06
155
0
Java里执行linux命令,关于死锁的问题

最近在用ffmpeg,所以经常会调用ffmpeg的一些命令,记录一下遇到的坑 如果要在Java中调用shell脚本时,可以使用Runtime.exec或ProcessBuilder.start。它们都会返回一个Process对象,通过这个...

TonyStarkSir
2018/07/28
0
0
java里执行linux命令,关于死锁的问题

最近在用Java调用ffmpeg的命令,所以记录下踩到的坑 如果要在Java中调用shell脚本时,可以使用Runtime.exec或ProcessBuilder.start。它们都会返回一个Process对象,通过这个Process可以对获取...

TonyStarkSir
2018/07/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

typescript 接口 函数类型 可索引类型

函数类型 可索引类型 数字索引签名 字符串索引签名 数字索引签名返回值 必须是 字符串索引签名返回值的子集 只读索引签名

lilugirl
今天
3
0
Oracle SQL语法实例合集

如需转载请注明出处https://my.oschina.net/feistel/blog/3052024 目的:迅速激活Oracle SQL 参考:《Oracle从入门到精通》 ------------------------------------------------------------......

LoSingSang
今天
2
0
增加 PostgreSQL 服务进程的最大打开文件数

https://serverfault.com/questions/628610/increasing-nproc-for-processes-launched-by-systemd-on-centos-7 要在systemd的配置里加才行...

helloclia
今天
2
0
组合模式在商品分类列表中的应用

在所有的树形结构中最适合的设计模式就是组合模式,我们看看常用商品分类中如何使用。 先定义一个树形结构的商品接口 public interface TreeProduct { List<TreeProduct> allProducts(...

算法之名
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部