2023年11月29日发(作者:)
java调⽤shell脚本和命令-ProcessBuilder⽅式
java调⽤shell脚本和命令-ProcessBuilder⽅式
在上⼀节中,我使⽤Runtime的⽅式,实现了对命令⾏和脚本⽂件的运⾏。最后我留下了⼀个⼩⼩的问题那就是这个InputStream和
errorStream需要分开获取,那有没有其他⽅,不⽤分开获取流呢?
1.相关⽂档
答案当然是有的,这就是这章节要使⽤的⽅式了。
ProcessBuilder
同样的,也先看下的API吧。
ProcessBuilder
类⽤于创建操作系统进程。
每个 实例管理⼀个进程属性集。) ⽅法利⽤这些属性创建⼀个新的 实例。) ⽅法可以从同⼀实例
ProcessBuilderstart()Processstart()
重复调⽤,以利⽤相同的或相关的属性创建新的⼦进程。
每个进程⽣成器管理这些进程属性:
命令
是⼀个字符串列表,它表⽰要调⽤的外部程序⽂件及其参数(如果有)。在此,表⽰有效的操作系统命令的字符串列表是依赖于系统的。例
如,每⼀个总体变量,通常都要成为此列表中的元素,但有⼀些操作系统,希望程序能⾃⼰标记命令⾏字符串——在这种系统中,Java 实现可能需
要命令确切地包含这两个元素。
环境变量值
是从 到 的依赖于系统的映射。初始值是当前进程环境的⼀个副本(请参阅 ())。
⼯作⽬录
。默认值是当前进程的当前⼯作⽬录,通常根据系统属性 来命名。
redirectErrorStream
属性。最初,此属性为 false,意思是⼦进程的标准输出和错误输出被发送给两个独⽴的流,这些流可以通过
utStream()) 和 orStream()) ⽅法来访问。如果将值设置为 true,标准错误将与标准输出合并。这使得关联错误
消息和相应的输出变得更容易。在此情况下,合并的数据可从 utStream()) 返回的流读取,⽽从 orStream()) 返回
的流读取将直接到达⽂件尾。
修改进程构建器的属性将影响后续由该对象的 ) ⽅法启动的进程,但从不会影响以前启动的进程或 Java ⾃⾝的进程。
start()
⼤多数错误检查由 ) ⽅法执⾏。可以修改对象的状态,但这样 ) 将会失败。例如,将命令属性设置为⼀个空列表将不会抛
start()start()
出异常,除⾮包含了 )。
start()
**注意,此类不是同步的。**如果多个线程同时访问⼀个 ,⽽其中⾄少⼀个线程从结构上修改了其中⼀个属性,它
ProcessBuilder
必须
保持外部同步。
要利⽤⼀组明确的环境变量启动进程,在添加环境变量之前,⾸先调⽤
()
这个API的解释中有⼀处特别的说明redirectErrorStream这个属性。这个设置为true即可实现流的合并操作。
看完了相关的⽅法的API吧。
构造⽅法摘要
ProcessBuilder(List
command) 利⽤指定的操作系统程序和参数构造⼀个进程⽣成器。
⽅法摘要
List
ProcessBuildercommand(List
ProcessBuilder command) 设置此进程⽣成器的操作系统程序和参数。
Filedirectory() 返回此进程⽣成器的⼯作⽬录。
ProcessBuilderdirectory(File directory) 设置此进程⽣成器的⼯作⽬录。
Map
booleanredirectErrorStream() 通知进程⽣成器是否合并标准错误和标准输出。
ProcessBuilderredirectErrorStream(boolean redirectErrorStream) 设置此进程⽣成器的 redirectErrorStream 属性。
Processstart() 使⽤此进程⽣成器的属性启动⼀个新进程。
Processstart() 使⽤此进程⽣成器的属性启动⼀个新进程。
⽅法摘要
好了接下来就开始代码实现了。
2.基础代码实现
这个过程与Runtime也基本⽆⼆异。就是将命令⾏参数传递给ProcessBuilder.将errorStream与inputStram合并,即设置
redirectErrorStream属性为true
ProcessBuilder processBuilder = new ProcessBuilder(command);
processBuilder.redirectErrorStream(true);
接下来就是将读取流的信息即可。
来看下完整的代码实现:
public class SynchronousLocalShellCommand {
private Logger logger = LoggerFactory.getLogger(SynchronousLocalShellCommand.class);
/** */
命令信息
private final String command;
public SynchronousLocalShellCommand(String command) {
this.command = command;
}
/**
*
执⾏命令并返回结果
*
* @return
命令执⾏结果
*/
public String doCommand() {
ProcessBuilder processBuilder = new ProcessBuilder(command);
try {
// ,Runtime
将错误输出流转移到标准输出流中但使⽤不可以
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
String dataMsg = reader(process.getInputStream());
int rsp = process.waitFor();
logger.info("run command {}, response {}", command, rsp);
return dataMsg;
} catch (IOException | InterruptedException e) {
logger.error("command : {} ,exception", command, e);
}
return null;
}
/**
*
数据读取操作
*
* @param input
输⼊流
*/
private String reader(InputStream input) {
StringBuilder outDat = new StringBuilder();
try (InputStreamReader inputReader = new InputStreamReader(input, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputReader)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
outDat.append(line);
outDat.append(Symbol.LINE);
}
} catch (IOException e) {
logger.error("command : {} ,exception", command, e);
}
return outDat.toString();
}
}
2.1 环境测试-windows
先来看下测试的代码:
public class TestSynchronousLocalShellCommand {
/** */
同步执⾏命令
@Test
public void synchornousDoCommand() {
//
运⾏⼀个正常的命令
this.runCommand("ping ");
// bat
运⾏⼀个脚本
this.runCommand("D:/run/bat/");
//
错误命令
this.runCommand("abcdef");
}
/**
* command
运⾏
*
* @param commandStr
错误命令⾏
*/
private void runCommand(String commandStr) {
SynchronousLocalShellCommand command = new SynchronousLocalShellCommand(commandStr);
String commandRsp = command.doCommand();
Assert.assertNotNull(commandRsp);
System.out.println(commandRsp);
}
}
2.2 ⾸先运⾏的问题
这⾥就可以看到结果:
11:57:58.177 [main] ERROR com.liujun.command.processbuilder.SynchronousLocalShellCommand - command : ping www.baidu.com ,exception
java.io.IOException: Cannot run program "ping ": CreateProcess error=2, 系统找不到指定的⽂件。
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at com.liujun.command.processbuilder.SynchronousLocalShellCommand.doCommand(SynchronousLocalShellCommand.java:44)
at com.liujun.command.processbuilder.TestSynchronousLocalShellCommand.runCommand(TestSynchronousLocalShellCommand.java:33)
at com.liujun.command.processbuilder.TestSynchronousLocalShellCommand.synchornousDoCommand(TestSynchronousLocalShellCommand.java:18)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
at mockit.integration.junit4.internal.JUnit4TestRunnerDecorator.executeTestMethod(JUnit4TestRunnerDecorator.java:162)
at mockit.integration.junit4.internal.JUnit4TestRunnerDecorator.invokeExplosively(JUnit4TestRunnerDecorator.java:71)
at mockit.integration.junit4.internal.MockFrameworkMethod.invokeExplosively(MockFrameworkMethod.java:37)
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java)
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
at com.intellij.rt.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:33)
at com.intellij.rt.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:230)
at com.intellij.rt.junit.JUnitStarter.main(JUnitStarter.java:58)
Caused by: java.io.IOException: CreateProcess error=2, 系统找不到指定的⽂件。
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(ProcessImpl.java:444)
at java.lang.ProcessImpl.start(ProcessImpl.java:140)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 29 common frames omitted
java.lang.AssertionError
at com.liujun.command.processbuilder.TestSynchronousLocalShellCommand.runCommand(TestSynchronousLocalShellCommand.java:34)
at com.liujun.command.processbuilder.TestSynchronousLocalShellCommand.synchornousDoCommand(TestSynchronousLocalShellCommand.java:18)
这个结果居然是提⽰找不到命令。那是哪⾥出现问题了?
2.3 问题的解决⽅案
像在命令⾏⼀般提⽰找不到指定⽂件,我的第⼀想法就是在系统上下⽂中找不到指定的运⾏⽂件。⽽像ping这样命令如果在windows运⾏
的话,第⼀步当然打开,然后输⼊ping 就能看到了。
经过这样⼀分析,我这个程序的运⾏是缺少了上下⽂了,那如何添加上下⽂呢?
当检查了ProcessBuilder后发现这个参数是接受⼀个集合为参数的,将命令⾏添加即可。但还存在⼀个问题,那就是当我⼿动执⾏时,是
分成了两个步骤先是打开cmd,然后输⼊ping命令。⽽我执⾏是需要⼀次执⾏命令的。这该怎么办呢?
这时候不访看看cmd的帮助⽂档?
C:Usersliujun>cmd /?
启动 Windows 命令解释器的⼀个新实例
CMD [/A | /U] [/Q] [/D] [/E:ON | /E:OFF] [/F:ON | /F:OFF] [/V:ON | /V:OFF]
[[/S] [/C | /K] string]
/C 执⾏字符串指定的命令然后终⽌
/C 执⾏字符串指定的命令然后终⽌
/K 执⾏字符串指定的命令但保留
/S 修改 /C 或 /K 之后的字符串处理(见下)
/Q 关闭回显
COLOR
⽽⽬录完成字符只符合⽬录名。
如果⽂件完成被⽤于内置式⽬录命令
public class SynchronousLocalShellCommand {
private Logger logger = LoggerFactory.getLogger(SynchronousLocalShellCommand.class);
/** */
命令信息
private final List<String> command;
public SynchronousLocalShellCommand(List<String> command) {
this.command = command;
}
/**
*
执⾏命令并返回结果
*
* @return
命令执⾏结果
*/
public String doCommand() {
ProcessBuilder processBuilder = new ProcessBuilder(command);
try {
// ,Runtime
将错误输出流转移到标准输出流中但使⽤不可以
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();
String dataMsg = reader(process.getInputStream());
int rsp = process.waitFor();
logger.info("run command {}, response {}", command, rsp);
return dataMsg;
} catch (IOException | InterruptedException e) {
logger.error("command : {} ,exception", command, e);
}
return null;
}
/**
*
数据读取操作
*
* @param input
输⼊流
*/
private String reader(InputStream input) {
StringBuilder outDat = new StringBuilder();
try (InputStreamReader inputReader = new InputStreamReader(input, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputReader)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
outDat.append(line);
outDat.append(Symbol.LINE);
}
} catch (IOException e) {
logger.error("command : {} ,exception", command, e);
}
return outDat.toString();
}
}
对测试代码也进⾏修改:
public class TestSynchronousLocalShellCommand {
/** */
同步执⾏命令
@Test
public void synchornousDoCommand() {
//
运⾏⼀个正常的命令
this.runCommand("ping ");
// bat
运⾏⼀个脚本
this.runCommand("D:/run/bat/");
//
错误命令
this.runCommand("abcdef");
}
/**
* command
运⾏
*
* @param commandStr
错误命令⾏
*/
private void runCommand(String commandStr) {
List<String> commandList = Arrays.asList("","/C",commandStr);
SynchronousLocalShellCommand command = new SynchronousLocalShellCommand(commandList);
String commandRsp = command.doCommand();
Assert.assertNotNull(commandRsp);
System.out.println(commandRsp);
}
}
当再次运⾏代码时:
还是存在乱码问题。
这个还是由于我本地windows使⽤的是GBK所引起的。那就先将解码改为GBK吧
public class SynchronousLocalShellCommand {
......
/**
*
数据读取操作
*
* @param input
输⼊流
*/
private String reader(InputStream input) {
StringBuilder outDat = new StringBuilder();
try (InputStreamReader inputReader = new InputStreamReader(input, Charset.forName("GBK"));
BufferedReader bufferedReader = new BufferedReader(inputReader)) {
String line;
while ((line = bufferedReader.readLine()) != null) {
outDat.append(line);
outDat.append(Symbol.LINE);
}
} catch (IOException e) {
logger.error("command : {} ,exception", command, e);
}
return outDat.toString();
}
}
当我再次运⾏时:
17:45:56.375 [main] INFO onousLocalShellCommand - run command [, /C, ping ], resp
onse 0
正在 Ping [112.80.248.75] 具有 32 字节的数据:
来⾃ 112.80.248.75 的回复: 字节=32 时间=13ms TTL=55
来⾃ 112.80.248.75 的回复: 字节=32 时间=15ms TTL=55
来⾃ 112.80.248.75 的回复: 字节=32 时间=13ms TTL=55
来⾃ 112.80.248.75 的回复: 字节=32 时间=13ms TTL=55
112.80.248.75 的 Ping 统计信息:
数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失),
往返⾏程的估计时间(以毫秒为单位):
最短 = 13ms,最长 = 15ms,平均 = 13ms
17:45:56.417 [main] INFO onousLocalShellCommand - run command [, /C, D:/run/bat/], respon
se 0
D:javaworkspaceselfworkdemojava8>echo C:javajdk8jdk1.8.0_241
C:javajdk8jdk1.8.0_241
D:javaworkspaceselfworkdemojava8>echo "hello world!!"
"hello world!!"
17:45:56.449 [main] INFO onousLocalShellCommand - run command [, /C, abcdef], response 1
'abcdef' 不是内部或外部命令,也不是可运⾏的程序
或批处理⽂件。
可以发现。这三个命令都已经按照预期来执⾏了。
虽然是按预期的执⾏了,但是与Runtime的执⾏⽅式相⽐。还是存在着不同的。当⼀条命令不存在时,runtime是抛出⼀个异常。⽽
ProcessBuilder则是在输出信息中提⽰了错误。
2.4 环境测试-linux
当我window测试完后成,就需要对linux做⼀个简单的测试了。也同样来看下linux测试的代码吧.
public class ProcessBuilderLinuxMain {
public static void main(String[] args) {
ProcessBuilderLinuxMain instance = new ProcessBuilderLinuxMain();
//
同步的执⾏
instance.synchornousDoCommand();
}
/** */
异步执⾏命令
private void synchornousDoCommand() {
this.runSyncCommand("ping -c 5 ");
this.runSyncCommand("/home/liujun/datarun/shell/");
this.runSyncCommand("adfadsfa");
}
/**
*
执⾏同步的命令操作
*
* @param commandStr
*/
private void runSyncCommand(String commandStr) {
List<String> commandList = Arrays.asList("bash", "-c", commandStr);
SynchronousLocalShellCommand command = new SynchronousLocalShellCommand(commandList);
String commandRsp = command.doCommand();
System.out.println("同步执⾏结果:" + commandRsp);
System.out.println("结束---------------");
}
}
有⼀个特别需要注意的问题那个就在linux上执⾏命令⼀般⽤的是bash。这个参数需要查下API。直接给结果。参数使⽤-c,
这个在linux下,编码需要切换下:
InputStreamReader inputReader = new InputStreamReader(input, StandardCharsets.UTF_8);
再来看下执⾏结果:
[liujun@fk03 datarun]$ java -cp :./lib/* sBuilderLinuxMain
19:20:20.855 [main] INFO onousLocalShellCommand - run command [bash, -c, ping -c 5 ], res
ponse 0
同步执⾏结果:PING (180.101.49.12) 56(84) bytes of data.
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=1 ttl=52 time=10.1 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=2 ttl=52 time=8.37 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=3 ttl=52 time=8.13 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=4 ttl=52 time=8.25 ms
64 bytes from 180.101.49.12 (180.101.49.12): icmp_seq=5 ttl=52 time=9.60 ms
--- ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 4006ms
rtt min/avg/max/mdev = 8.135/8.900/10.135/0.819 ms
结束---------------
19:20:20.866 [main] INFO onousLocalShellCommand - run command [bash, -c, /home/liujun/datarun/shell/run.
sh], response 0
同步执⾏结果:Hello World !
结束---------------
19:20:20.869 [main] INFO onousLocalShellCommand - run command [bash, -c, adfadsfa], response 127
同步执⾏结果:bash: adfadsfa: command not found
结束---------------
[liujun@fk03 datarun]$
通过观察发现,三条命令都会得到相应的结果。
3.需要异步执⾏的场景
当命令长时间执⾏或者有的脚本边执⾏边有信息输出时,我们就不能等着命令执⾏结束了。再才给出提⽰。⽽是应该边执⾏就边给出提⽰。
那还是先来看看代码实现吧。
3.1 代码实现
public class AsynchronousLocalShellCommand implements Runnable {
private Logger logger = LoggerFactory.getLogger(AsynchronousLocalShellCommand.class);
/** */
命令信息
private final List<String> command;
/** */
运⾏的处理流程
private Process process;
/** */
数据的输出流
private InputStream input;
/** */
输出信息
private final StringBuilder outDat = new StringBuilder();
/** ,-1 0 */
进程执⾏结束后的结果初始化,,成功执⾏结束
private AtomicInteger processOutCode = new AtomicInteger(-1);
/** */
运⾏标识
private AtomicBoolean runFlag = new AtomicBoolean(true);
public AsynchronousLocalShellCommand(List<String> command) {
this.command = command;
}
/** */
开始执⾏命令
public void doCommand() {
try {
ProcessBuilder processBuilder = new ProcessBuilder(command);
// ,Runtime
将错误输出流转移到标准输出流中但使⽤不可以
processBuilder.redirectErrorStream(true);
this.process = processBuilder.start();
} catch (IOException e) {
logger.error("command : {} ,exception", command, e);
}
// Process
获取⼦进程的输⼊流。输⼊流获得由该对象表⽰的进程的标准输出流。
input = process.getInputStream();
//
成功时才将任务提交线程池运⾏
TaskThreadDataPool.INSTANCE.submit(this);
}
@Override
public void run() {
//
进⾏数据读取操作
reader(this.input);
int rsp = -1;
try {
// exitValue()
等待进程结束,不能直接调⽤,这将导致还没有运⾏完成就返回了结果
rsp = process.waitFor();
} catch (InterruptedException e) {
logger.error("run command {} InterruptedException", command, rsp);
//
停⽌操作
rsp = -5;
}
processOutCode.set(rsp);
logger.info("run command {}, response {}", command, rsp);
}
/**
*
获取执⾏结果
*
* @return
*/
public String getOutDat() {
return outDat.toString();
}
/**
*
返回进程执⾏的结果码
*
* @return
*/
public int rspProcess() {
return processOutCode.get();
}
/**
*
停⽌操作
*
* @param
*/
public void stop() {
//
停⽌读取输出操作
this.runFlag.set(false);
this.runFlag.set(false);
//
停⽌⼦进程
process.destroy();
}
/**
*
数据读取操作
*
* @param input
输⼊流
*/
private void reader(InputStream input) {
try (InputStreamReader inputReader = new InputStreamReader(input, StandardCharsets.UTF_8);
BufferedReader bufferedReader = new BufferedReader(inputReader)) {
String line;
while ((line = bufferedReader.readLine()) != null && runFlag.get()) {
outDat.append(line);
outDat.append(Symbol.LINE);
}
} catch (IOException e) {
logger.error("command : {} ,exception", command, e);
}
}
}
3.2 环境测试-windows
windows环境还是使⽤单元测试:
public class TestAsynchronousLocalShellCommand {
/** */
测试异步命令的执⾏
@Test
public void asynchronous() {
//
运⾏⼀个正常的命令
this.runCommand("ping ");
// bat
运⾏⼀个脚本
this.runCommand("D:/run/bat/");
//
错误命令
this.runCommand("abcdef");
TaskThreadDataPool.INSTANCE.shutdown();
}
private void runCommand(String commandStr) {
List<String> commandList = Arrays.asList("", "/c", commandStr);
AsynchronousLocalShellCommand command = new AsynchronousLocalShellCommand(commandList);
command.doCommand();
for (int i = 0; i < 3; i++) {
int code = command.rspProcess();
String outData = command.getOutDat();
Assert.assertNotNull(outData);
System.out.println("第" + i + ",结果码,code:" + code + ",rspmsg:" + outData);
ThreadUtils.sleep(1);
}
//
不再运⾏
command.stop();
int code = command.rspProcess();
String outData = command.getOutDat();
System.out.println("命令" + commandStr + ",结果码,code:" + code + ",rspmsg:" + outData);
System.out.println("结束----------");
}
}
同样的先来看看测试代码的运⾏结果:
第0,结果码,code:-1,rspmsg:
第1,结果码,code:-1,rspmsg:
正在 Ping www.a.shifen.com [180.101.49.11] 具有 32 字节的数据:
来⾃ 180.101.49.11 的回复: 字节=32 时间=12ms TTL=50
第2,结果码,code:-1,rspmsg:
正在 Ping www.a.shifen.com [180.101.49.11] 具有 32 字节的数据:
来⾃ 180.101.49.11 的回复: 字节=32 时间=12ms TTL=50
来⾃ 180.101.49.11 的回复: 字节=32 时间=12ms TTL=50
命令ping www.baidu.com,结果码,code:-1,rspmsg:
正在 Ping www.a.shifen.com [180.101.49.11] 具有 32 字节的数据:
来⾃ 180.101.49.11 的回复: 字节=32 时间=12ms TTL=50
来⾃ 180.101.49.11 的回复: 字节=32 时间=12ms TTL=50
来⾃ 180.101.49.11 的回复: 字节=32 时间=13ms TTL=50
结束----------
第0,结果码,code:-1,rspmsg:
19:50:08.443 [command-2] INFO com.liujun.command.processbuilder.AsynchronousLocalShellCommand - run command [cmd.exe, /c, D:/run/bat/run.bat],
response 0
19:50:08.491 [command-1] INFO com.liujun.command.processbuilder.AsynchronousLocalShellCommand - run command [cmd.exe, /c, ping www.baidu.co
m], response 1
第1,结果码,code:0,rspmsg:
D:javaworkspaceselfworkdemojava8>echo C:javajdk8jdk1.8.0_241
C:javajdk8jdk1.8.0_241
D:javaworkspaceselfworkdemojava8>echo "hello world!!"
"hello world!!"
第2,结果码,code:0,rspmsg:
D:javaworkspaceselfworkdemojava8>echo C:javajdk8jdk1.8.0_241
C:javajdk8jdk1.8.0_241
D:javaworkspaceselfworkdemojava8>echo "hello world!!"
"hello world!!"
命令D:/run/bat/run.bat,结果码,code:0,rspmsg:
D:javaworkspaceselfworkdemojava8>echo C:javajdk8jdk1.8.0_241
C:javajdk8jdk1.8.0_241
D:javaworkspaceselfworkdemojava8>echo "hello world!!"
"hello world!!"
结束----------
第0,结果码,code:-1,rspmsg:
19:50:11.444 [command-2] INFO com.liujun.command.processbuilder.AsynchronousLocalShellCommand - run command [cmd.exe, /c, abcdef], response
1
第1,结果码,code:1,rspmsg:'abcdef' 不是内部或外部命令,也不是可运⾏的程序
或批处理⽂件。
第2,结果码,code:1,rspmsg:'abcdef' 不是内部或外部命令,也不是可运⾏的程序
或批处理⽂件。
命令abcdef,结果码,code:1,rspmsg:'abcdef' 不是内部或外部命令,也不是可运⾏的程序
或批处理⽂件。
结束----------
注:windows平台上编码使⽤
观察代码的执⾏结果 。可发现命令都正确的执⾏。并得到了预期的结果。
3.3 环境测试-Linux测试
那接下来就是linux环境环境测试了。
还是先看代码:
public class ProcessBuilderLinuxMain {
public static void main(String[] args) {
ProcessBuilderLinuxMain instance = new ProcessBuilderLinuxMain();
//
异步的执⾏
instance.asynchronousDoCommand();
}
/** */
异步执⾏命令
private void asynchronousDoCommand() {
//
执⾏⼀个正常的命令,带终⽌
this.runAsyncCommand("ping ");
//
运⾏⼀个脚本⽂件
this.runAsyncCommand("/home/liujun/datarun/shell/");
//
执⾏⼀个错误命令
this.runAsyncCommand("adfadsfa");
TaskThreadDataPool.INSTANCE.shutdown();
}
/**
*
异步的执⾏操作
*
* @param commandStr
*/
private void runAsyncCommand(String commandStr) {
List<String> commandList = Arrays.asList("bash", "-c", commandStr);
AsynchronousLocalShellCommand command = new AsynchronousLocalShellCommand(commandList);
command.doCommand();
for (int i = 0; i < 3; i++) {
int code = command.rspProcess();
String outData = command.getOutDat();
System.out.println("当前第:" + i + "次,code:" + code + ",响应结果 :" + outData);
ThreadUtils.sleep(1);
}
//
不再运⾏
command.stop();
int code = command.rspProcess();
String outData = command.getOutDat();
System.out.println("结果状态:" + code + ",执⾏结果:" + outData);
System.out.println("结束--------");
}
}
然后看下结果:
[liujun@fk03 datarun]$ java -cp :./lib/* sBuilderLinuxMain
当前第:0次,code:-1,响应结果 :
当前第:1次,code:-1,响应结果 :PING (180.101.49.11) 56(84) bytes of data.
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=1 ttl=52 time=9.35 ms
当前第:2次,code:-1,响应结果 :PING (180.101.49.11) 56(84) bytes of data.
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=1 ttl=52 time=9.35 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=2 ttl=52 time=10.0 ms
结果状态:-1,执⾏结果:PING (180.101.49.11) 56(84) bytes of data.
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=1 ttl=52 time=9.35 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=2 ttl=52 time=10.0 ms
64 bytes from 180.101.49.11 (180.101.49.11): icmp_seq=3 ttl=52 time=9.22 ms
结束--------
当前第:0次,code:-1,响应结果 :
21:55:53.227 [command-2] INFO ronousLocalShellCommand - run command [bash, -c, /home/liujun/datarun/s
hell/], response 0
21:55:53.226 [command-1] INFO ronousLocalShellCommand - run command [bash, -c, ping ],
response 143
当前第:1次,code:0,响应结果 :Hello World !
当前第:2次,code:0,响应结果 :Hello World !
结果状态:0,执⾏结果:Hello World !
结束--------
当前第:0次,code:-1,响应结果 :
public class Runtime {
......
public Process exec(String command) throws IOException {
return exec(command, null, null);
}
......
public Process exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
......
public Process exec(String[] cmdarray, String[] envp, File dir)
throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
......
}
最后这个Runtime调⽤的也是ProcessBuilder只是加⼊了环境信息。和相关的⽬录信息。
5.总结
通过本章节,我对使⽤ProcessBuilder做了⼀个⽐较详细的解释。也对其相关的场景做了⼀些简单的测试。通过两种⽅式的对⽐及实现。
在实际的项⽬中我还是推荐使⽤ProcessBuilder,这种⽅式可以将错误流与正常流进⾏合并,操作上带来了⼀些便利。


发布评论