文
章
目
录
章
目
录
Java开发的项目,部署到Linux系统后,偶尔会使用Java调用Shell命令和脚本,比如修改了某些参数配置需要重启tomcat服务器或者重启系统等等,虽然使用频率不高,但是我们还是有必要了解下如何使用Java执行Shell命令和Shell脚本。JDK给我们提拱了两种实现方案,一种是Runtime.getRuntime().exec()
API方法,另一种是通过ProcessBuilder
来实现该功能,其中ProcessBuilder
更灵活也更可取。
一、Runtime方法:
1、介绍
我们先了解下其具体的方法有哪些:
// 在单独的进程中执行指定的字符串命令 Process exec(String command) // 在单独的进程中执行指定命令和变量 Process exec(String[] cmdarray) // 在指定环境的独立进程中执行指定命令和变量 Process exec(String[] cmdarray, String[] envp) // 在指定环境和工作目录的独立进程中执行指定的命令和变量 Process exec(String[] cmdarray, String[] envp, File dir) // 在指定环境的单独进程中执行指定的字符串命令 Process exec(String command, String[] envp) // 在有指定环境和工作目录的独立进程中执行指定的字符串命令 Process exec(String command, String[] envp, File dir)
说明下参数含义:
1)command
: 一条指定的系统命令,比如 : echo hello。
2)cmdarray
: 包含所调用命令及其参数的数组。
3)envp
: 字符串数组,其中每个元素的环境变量的设置格式为name=value
;如果子进程应该继承当前进程的环境,则该参数为 null。
4)dir
: 子进程的工作目录;如果子进程应该继承当前进程的工作目录,则该参数为 null。
5)Process
:调用exec方法后JVM会启动一个Precess进程返回, 我们可以调用如下方法:
// 检测执行是否正确。该方法使当前线程等待,返回0 表示正常终止;否则,就表示异常失败 abstract int waitFor() // 获取返回值。通过输入流获取脚本会指令输出的值。 abstract InputStream getInputStream()
2、执行shell命令
public void callCmd(String command){ try { Process process = Runtime.getRuntime().exec(command); int status = process.waitFor(); if(status == 0){ System.out.println("执行成功"); }else { System.out.println("执行失败"); } }catch (Exception e){ e.printStackTrace(); } }
3、执行shell脚本
// path为shell脚本绝对路径 public String callScript(String path) { String result = null; BufferedReader br = null; try { Process ps = Runtime.getRuntime().exec(path); ps.waitFor(); br = new BufferedReader(new InputStreamReader(ps.getInputStream())); StringBuffer sb = new StringBuffer(); String line; while ((line = br.readLine()) != null) { sb.append(line).append("\n"); } result = sb.toString(); } catch (Exception e) { e.printStackTrace(); }finally { try { br.close(); } catch (IOException e) { e.printStackTrace(); } } return result; }
其他的api方法就不再演示了,可以自行研究下。
二、ProcessBuilder方法:
ProcessBuilder相比Runtime方法更可取,因为能够定制一些细节。例如:
- 使用builder.directory()方法,改变正在运行Shell命令的工作目录
- 使用builder.environment()方法,设置自定义键值对作为环境变量
- 重定向输入和输出流值自定义流
- 使用build.inheritio()方法将它们都继承到当前JVM进程的流中
ProcessBuilder执行主要分成三步:
- 构建ProcessBuilder
- 构建外部命令
- 执行start
案例代码:
1、执行普通指令
// 使用touch在/usr/test下新建my.txt文件 public void test() throws IOException, InterruptedException { // 构建一个命令 List<String> command = new ArrayList<>(); // 该命令的位置,可以用 which touch 查找 command.add("/usr/bin/touch"); command.add("my.txt"); // 构建一个 processBuilder ProcessBuilder processBuilder = new ProcessBuilder(); // 切换工作目录,也可以不写这个,直接在command 里面 加上 processBuilder.directory(new File("/usr/test")); // command.add("/usr/test") // 添加命令 processBuilder.command(command); // 执行 Process start = processBuilder.start(); if (start.isAlive()) { start.waitFor(); } }
2、执行有输出内容的指令
public void test2() throws IOException, InterruptedException { // 构建一个命令 List<String> command = new ArrayList<>(); // 该命令的位置,可以用 which 查找 command.add("/bin/ls"); ProcessBuilder processBuilder = new ProcessBuilder(); processBuilder.directory(new File("/usr/test")); // 设置为 true 后,错误会和标准输出一样输出 processBuilder.redirectErrorStream(true); processBuilder.command(command); Process process = processBuilder.start(); InputStream is = process.getInputStream(); // 使用 hutool 工具类 解析 inputStream FastByteArrayOutputStream read = IoUtil.read(is); System.out.println(read); is.close(); if(process.isAlive()){ process.waitFor(); } }
三、总结
以上就是关于如何使用Java执行Shell命令和Shell脚本的内容,有些具体的操作还需要自己在实践中摸索,不断学习,才能更好地运用自如。