Java执行操作系统命令-Process抽象类(2)(Linux)
Java 8
20.04.1-Ubuntu
Eclipse Version: 2022-09 (4.25.0)
--
前一篇是在Windows上执行命令,本篇介绍在Linux系统中执行命令。
测试命令:
ls、cd、pwd
测试代码
ProcessController.java:提供接口 /api/process/executeCmd;
ExecuteCmdDTO.java:接收参数,仅一个 cmd 字符串;
@RestController
@RequestMapping(value = "/api/process")
@Slf4j
public class ProcessController {
@PostMapping(value = "/executeCmd")
public Boolean executeCmd(@RequestBody ExecuteCmdDTO dto) {
log.info("ExecuteCmdDTO dto={}", dto);
if (StringUtils.isEmpty(dto.getCmd())) {
return false;
}
ProcessLinuxMain.test2(dto.getCmd());
return Boolean.TRUE;
}
}
@Data
public class ExecuteCmdDTO {
/**
* 命令
*/
private String cmd;
}
ProcessLinuxMain.java:使用 Runtime.getRuntime().exec(cmd) 执行命令
public class ProcessLinuxMain {
/**
* 测试:Runtime.getRuntime().exec(...)
*/
public static void test2(String cmd) {
cs.accept("start test2..cmd=" + cmd);
try {
Process ps = Runtime.getRuntime().exec(cmd);
cs.accept("1、InputStream:");
processIs(ps.getInputStream());
cs.accept("2、ErrorStream:");
processIs(ps.getErrorStream());
cs.accept("3、----END----");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 输出InputStream的内容:字符集 GB2312
* @param is
* @throws IOException
*/
private static void processIs(InputStream is) throws IOException {
final int size = 1024;
byte[] buffer = new byte[size];
Charset cset = Charset.forName("GB2312");
cs.accept("cset=" + cset);
StringBuffer sb = new StringBuffer();
int ret = 0;
int total = 0;
while ((ret = is.read(buffer, 0, size)) > 0) {
total += ret;
String chunk = new String(buffer, 0, ret, cset);
sb.append(chunk);
}
cs.accept("ret=" + ret + ", total=" + total);
cs.accept("sb=" + sb.length() + ", content=" + sb);
is.close();
}
}
ben发布于博客园
测试方式:
测试目录:
/home/ben/javaProcess
ben@ben-u:~/javaProcess$ pwd
/home/ben/javaProcess
ben@ben-u:~/javaProcess$ ls
sshdemo-0.0.1-SNAPSHOT.jar
ben发布于博客园
执行:ls /
结果:成功
结果1
ExecuteCmdDTO dto=ExecuteCmdDTO(cmd=ls /)
start test2..cmd=ls /
1、InputStream:
cset=GB2312
ret=-1, total=134
sb=134, content=bin
boot
cdrom
dev
etc
home
lib
lib32
lib64
libx32
lost+found
media
mnt
opt
proc
repo
root
run
sbin
snap
srv
swapfile
sys
tmp
usr
var
2、ErrorStream:
cset=GB2312
ret=-1, total=0
sb=0, content=
3、----END----
执行:cd /home/ben/javaProcess
结果:失败。
提示:java.io.IOException: Cannot run program "cd": error=2, No such file or directory
解决:
前面添加 /bin/bash -c
注意,是小写的 c。ben发布于博客园
执行:/bin/bash -c cd /home/ben/javaProcess
结果:成功。
不过,InputStream、ErrorStream 没有数据。
执行:/bin/bash -c cd /home/ben/javaProcess | ls
结果:失败——没有收到 ls 命令的结果。ben发布于博客园
解决:
见下文使用。
执行:pwd
结果:成功。
执行:/bin/bash -c ls /
结果:失败。
执行 ls 命令时,显示了 当前目录,而不是 预期的根目录。ben发布于博客园
小结(1)
ls 命令前 不加 /bin/bash -c;
cd 命令前 加 /bin/bash -c;
pwd 命令前 可加 可不加,都行。ben发布于博客园
为什么呢?TODO
Runtime#exec函数分析
上面测试的函数 都使用的下面的函数:ben发布于博客园
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 {
//...
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类的源码来看,其exec最终是执行 ProcessBuilder 的 start()。
疑问:
参数 envp、dir 有什么用呢?TODOben发布于博客园
测试:Runtime#exec(String cmdarray[]) 函数
入参 为字符串数组了。
执行:ls
入参:
[
"ls"
]
成功。ben发布于博客园
执行:pwd
入参:
[
"pwd"
]
成功。
执行:ls /
入参:
[
"ls /"
]
失败。
执行:["ls","/"]
成功。
执行多条命令:ls、grep
终端执行效果:
入参:
[
"ls",
"/",
"|",
"grep",
"lib"
]
失败:grep的结果没出来,因为识别不了 竖线。ben发布于博客园
加上前缀:/bin/bash -c
入参:
[
"/bin/bash",
"-c",
"ls",
"/",
"|",
"grep",
"lib"
]
失败:和终端执行结果不同。ben发布于博客园
再次测试
入参:
[
"/bin/bash",
"-c",
"ls / | grep lib"
]
成功:输出结果为最终结果。ben发布于博客园
更进一步:成功。ben发布于博客园
小结(2)
执行带管道的多条命令,得使用 exec(String cmdarray[]) 函数,还得加上 /bin/bash, -c 两个字符串。
如果将 /bin/bash, -c 合并为一个字符串,则执行失败:
在本文的Ubuntu中,除了 /bin/bash,还可以使用 /bin/sh、/bin/dash。
其中,sh 指向 dash。ben发布于博客园
下一步:
还需要对命令执行的原理做深入了解才是。
本文链接:
https://www.cnblogs.com/luo630/p/16994832.html
参考资料
1、Java调用Linux命令(cd的处理)ben发布于博客园
https://www.cnblogs.com/yoyotl/p/6914096.html
posted @ 2017-05-30 22:17 一沙世界
2、
ben发布于博客园