Java调用linux 管道命令 & 白名单配置

  • Post author:
  • Post category:java



一  执行单条管道命令

适用于单个管道命令的场景,如查进程、杀进程、全局命令等的调用;

	/**
	 * 单条管道命令
	 * @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();
	}

上述代码存在的缺陷:

1、管道命令之间相互独立,上述方法不支持执行连续管道命令,eg:1、cd 目录  2、./test.sh,此场景考虑使用下述批量管道命令执行方式;

2、遗留问题:上述方法中由于使用了Thread打印流日志,可能会导致主方法executeCMD()调用到pr.destroy();时独立线程中流日志尚未输出结束,此时getInputStreamResponse()方法中会报 stream closed的异常,由于我的业务中不关注输出结果,所以此问题暂未修复,有需要的小伙伴麻烦自行解决啦;


二  批量管道命令



适用于相互之间有依赖性的命令执行,如先进入某个目录,再在此目录下执行命令的场景;

	/**
	 * 批量执行管道命令
	 * @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();
			}
		}
	}


三  管道命令白名单配置

主要是为了防止命令注入,所以加个白名单配置

	/**
	 * 管道命令白名单
	 * @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();
	}



版权声明:本文为sxg0205原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。