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) 利⽤指定的操作系统程序和参数构造⼀个进程⽣成器。

command) 利⽤指定的操作系统程序和参数构造⼀个进程⽣成器。

⽅法摘要

Listcommand() 返回此进程⽣成器的操作系统程序和参数。

ProcessBuildercommand(List command) 设置此进程⽣成器的操作系统程序和参数。

ProcessBuilder command) 设置此进程⽣成器的操作系统程序和参数。

Filedirectory() 返回此进程⽣成器的⼯作⽬录。

ProcessBuilderdirectory(File directory) 设置此进程⽣成器的⼯作⽬录。

Mapenvironment() 返回此进程⽣成器环境的字符串映射视图。

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,这种⽅式可以将错误流与正常流进⾏合并,操作上带来了⼀些便利。