1 Execute a single pipeline command
Applicable to the scenario of a single pipeline command, such as checking process, killing process, calling global commands, etc.;
/**
* 单条管道命令
* @param command 单条管道命令
*/
public static void executeCMD(String command) {
String safeCommand = getSafeCommand(command, "/bin/sh -c ");
Process pr = null;
try {
pr = Runtime.getRuntime().exec(safeCommand, null, new File(Constants.PATH_SLASH));
// 读取输出,脚本运行结束后获取返回值
// 防阻塞,起独立线程获取输出流及错误流
getInputStreamResponse(pr.getInputStream(), "InputStream");
getInputStreamResponse(pr.getErrorStream(), "ErrorStream");
// 等待进程执行结束并销毁
pr.waitFor();
} catch (IOException | InterruptedException e) {
throw new RuntimeException("executeCMD process error", e);
} finally {
pr.destroy();
}
}
private static void getInputStreamResponse(InputStream is, String streamType) {
new Thread(() -> {
try {
BufferedReader stdInput = new BufferedReader(new InputStreamReader(is));
StringBuffer strBuf = new StringBuffer();
String line;
while ((line = stdInput.readLine()) != null) {
log.info(line);
}
} catch (Exception e) {
log.error(e);
} finally {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
}
Defects in the above code:
1. The pipeline commands are independent of each other. The above method does not support the execution of continuous pipeline commands, eg: 1. cd directory 2. ./test.sh. In this scenario, consider using the following batch pipeline command execution method;
2. Remaining issues: In the above method, because Thread is used to print stream logs, the main method executeCMD() may be called to pr.destroy(); when the stream log output in the independent thread has not yet finished, the getInputStreamResponse() method will report a stream closed exception. Since I don’t pay attention to the output results in my business, this problem has not been fixed yet, and small partners in need can solve it by themselves;
Two batch pipeline commands
Applicable to the execution of commands that are dependent on each other, such as entering a certain directory first, and then executing commands in this directory;
/**
* 批量执行管道命令
* @param commands 批量管道命令
*/
public static void executeBatchCMD(String[] commands) {
Runtime run = Runtime.getRuntime();
File wd = new File("/bin");
Process proc = null;
try {
proc = run.exec("/bin/bash", null, wd);
} catch (IOException e) {
e.printStackTrace();
}
if (proc != null) {
BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));
PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);
for (String command : commands) {
out.println(getSafeCommand(command, ""));
}
out.println(getSafeCommand("exit", ""));
try {
String line;
while ((line = in.readLine()) != null) {
log.info(line);
}
proc.waitFor();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
in.close();
} catch (IOException e) {}
out.close();
proc.destroy();
}
}
}
Three pipeline command whitelist configuration
Mainly to prevent command injection, so add a whitelist configuration
/**
* 管道命令白名单
* @param command 管道命令
* @param suffix
* @return
*/
public static String getSafeCommand(String command, String suffix) {
//command = "/bin/sh -c "+command;
command = suffix + command;
StringBuffer safeCommand = new StringBuffer();
String whiteList = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890-=[]\\',./";
char[] safeCommandChars = command.toCharArray();
for (int i = 0, length = safeCommandChars.length; i < length; i++) {
int whiteListIndex = whiteList.indexOf(safeCommandChars[i]);
if (-1 == whiteListIndex) {
return safeCommand.toString();
}
safeCommand.append(whiteList.charAt(whiteListIndex));
}
return safeCommand.toString();
}