使用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只是摘录了一部分。

  • JAVA调用Shell脚本
  • java调用shell命令并获取执行结果
  • Java Process中waitFor()的问题
  • 怎么通过java去调用并执行shell脚本以及问题总结
  • Java Runtime.exec()的使用
  • JAVA中Runtime类以及exec()方法,Process的使用

查看原文: http://www.jackieathome.net/archives/456.html

使用Java调用shell脚本时遇到的问题相关推荐

  1. java无阻塞执行脚本,JAVA调用Shell脚本-及阻塞的解决方法

    JAVA调用Shell脚本--及阻塞的解决办法 用java调用shell,使用 Process p=Runtime.getRuntime().exec(String[] cmd); Runtime.e ...

  2. java调用shell脚本并传递参数

    最近业务上需要java调用执行shell脚本进行一些业务处理,写了个demo,记录下. 主要代码 @RequestMapping("/copy/database")@Respons ...

  3. java调用shell脚本及注意事项

    需求: get方法下载远程zip包,然后zip包解压,取出第一级目录再次进行压缩获取新的压缩zip包. 问题: 如果选择使用java代码的IO流操作,在不确定zip包大小的情况下可能会占用很大的内存, ...

  4. [转载]JAVA调用Shell脚本

    FROM:http://blog.csdn.net/jj12345jj198999/article/details/11891701 在实际项目中,JAVA有时候需要调用C写出来的东西,除了JNI以外 ...

  5. java调用shell脚本_Java 执行Shell脚本指令

    一.介绍 有时候我们在Linux中运行Java程序时,需要调用一些Shell命令和脚本.而Runtime.getRuntime().exec()方法给我们提供了这个功能,而且Runtime.getRu ...

  6. Java 调用Shell脚本

    最近我的项目要我在WebService里用Java调用Linux下的Shell 脚本,在网上找了一些资料,以供学习. 地址:http://brian.pontarelli.com/2005/11/11 ...

  7. Java 调用Shell脚本执行 SCP命令提示Authorized users only. All activity may be monitored and reported.

    近期做了个小项目主要是关于数据处理这方面的. 在Java后端调用服务器上Shell脚本,而Shell脚本执行时一条Scp执行结果的提示报 Authorized users only. All acti ...

  8. Java调用 shell脚本阻塞

    Java在调用Shell时,要不断读取进程中标准输出和错误输出流的信息,否则缓冲区被写满就会造成子进程阻塞而无法继续运行下去,可起两个线程不断读取标准输出.错误流信息而不被阻塞 import java ...

  9. python 调用shell 不阻塞_遇到问题---python调用shell脚本时subprocess.check_call不阻塞

    遇到的问题 使用命令 subprocess.check_call(cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) ...

  10. java调用shell脚本,解决传参和权限问题

    1. java 执行shell java 通过 Runtime.getRuntime().exec() 方法执行 shell 的命令或 脚本,exec()方法的参数可以是脚本的路径也可以是直接的 sh ...

最新文章

  1. 二十四、Struts2中的UI标签
  2. 【转】C#对象的深拷贝与浅拷贝
  3. linux ping IP地址与telnet 测试IP端口
  4. 一月书单(1/25 update)
  5. 零基础 | 入行软件测试,你想知道的都在这里了
  6. JAVA语言的类、对象、变量、方法等的概括说明
  7. 东农计算机网络技术离线作业,东农16秋《电力系统分析》在线作业
  8. 解决窗口桌面管理器内存占用过高,系统更新,核显驱动异常造成的内存泄漏问题。
  9. Unity如何查看当前内存使用情况
  10. 你们这些90后,都是什么神仙小精灵?
  11. 工业云计算技术在工业自动化系统中的作用
  12. 手机浏览器/H5页面实现打开微信代码 引导关注公众号
  13. 群晖docker容器内配置ubuntu远程桌面访问
  14. 怎么在小程序中使用彩色图标iconfont
  15. 【总结】最系统化的CV内容,有三AI所有免费与付费的计算机视觉课程汇总(2022年12月)...
  16. 2022年分布式I/O市场前景分析及研究报告
  17. WPF引入OCX控件
  18. dhcp协议服务器怎么设置,DHCP服务器如何设置?
  19. OC--category(类目、分类、类别)
  20. 什么是恒流源?如何工作的?

热门文章

  1. win10电脑wifi服务器未响应,win10系统点电脑无线图标没反应的解决方法
  2. photoshop笔记
  3. jQuery 之过滤选择器
  4. 腾讯art-template4,即vue后又获一利器
  5. Template.js
  6. 统帅转型:轻时尚时代挺进年轻领地
  7. 剖析美国大片《西部世界》 嵌入式技术应用新高度
  8. 最大公约数简便算法_三种求最大公约数的方法
  9. 深入理解AX Inventory Aging Report
  10. 如何用阿里云云盘快照恢复部分数据