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发布于博客园

 

posted @ 2022-12-21 19:28  快乐的凡人721  阅读(166)  评论(0编辑  收藏  举报