java:执行linux sudo命令

37 篇文章 1 订阅

我们知道java中执行控制台命令,都是通过 Runtime.exec系列方法。
如果要执行root权限的命令需要用到sudo,需要输入sudo密码,这个也好解决,使用echo可以向sudo输入密码,同时sudo要加-S参数指定从标准输入读取密码,示例如下:

echo ‘sudopassword’ | sudo -S cat /etc/profile

但是在java中通过 Runtime.exec方法执行上面的命令,还是会无效。错误提示可能是。

sudo:抱歉,您必须拥有一个终端来执行 sudo
sudo: sorry, you must have a tty to run sudo
sudo:没有终端存在,且未指定 askpass 程序
sudo: no tty present and no askpass program specified

解决这个问题需要修改/etc/sudoers

# 给 sudoers 增加写权限
sudo chmod +w /etc/sudoers
# 编辑 sudoers
vi /etc/sudoers

找到 Defaults requiretty改为Defaults !requiretty或者直接注释掉#Defaults requiretty
找到Defaults !visiblepw改为Defaults visiblepw或者增一行 Defaults visiblepw
如下图。
这里写图片描述

修改后 wq保存,记得要sudo chmod -w /etc/sudoers删除写权限

为了简化sudo命令的执行,我封装一了个CmdExceuor类,允许执行多条命令。

代码仓库地址:
https://gitee.com/l0km/common-java/blob/master/common-base/src/main/java/net/gdface/utils/CmdExecutor.java

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

/**
 * linux命令行执行器
 * @author guyadong
 *
 */
public class CmdExecutor{
	private static final Logger logger = Logger.getLogger(CmdExecutor.class.getSimpleName());
	private static final String SUDO_CMD = "sudo";
	private static final String SHELL_NAME = "/bin/bash";
	private static final String SHELL_PARAM = "-c";
	private static final String REDIRECT = "2>&1";
	/** 执行 sudo 的密码 */
	private final String sudoPassword;
	/** 是否显示命令内容及输出 */
	private boolean verbose=true;
	/** 是否错误输出重定向 */
	private boolean errRedirect = true;
	/** 是否同步,主线程是否等待命令执行结束 */
	private boolean sync = true;
	/** 执行多条命令时的命令分隔符 */
	private String cmdSeparator = " && ";
	private List<String> cmds = new ArrayList<String>(16);
	public static CmdExecutor builder(){
		return new CmdExecutor();
	}
	public static CmdExecutor builder(String sudoPasword){
		return new CmdExecutor(sudoPasword);
	}
	protected CmdExecutor(){
		this(null);
	}
	protected CmdExecutor(String sudoPasword){
		this.sudoPassword = sudoPasword;
	}
	public CmdExecutor verbose(boolean verbose){
		this.verbose = verbose;
		return this;
	}
	public CmdExecutor errRedirect(boolean errRedirect){
		this.errRedirect = errRedirect;
		return this;
	}
	public CmdExecutor sync(boolean sync){
		this.sync = sync;
		return this;
	}
	public CmdExecutor cmdSeparator(String cmdSeparator){
		if(null != cmdSeparator && !cmdSeparator.isEmpty()){
			this.cmdSeparator = cmdSeparator;
		}
		return this;
	}
	private String getRedirect(){
		return errRedirect ? REDIRECT : "";
	}
	/**
	 * 添加一条需要sudo执行的命令
	 * @param cmd 要执行的命令(字符串中不需要有sudo)
	 * @return
	 */
	public CmdExecutor sudoCmd(String cmd){
		if(null != cmd && 0 != cmd.length()){
			if(null == sudoPassword){
				cmds.add(String.format("%s %s %s",SUDO_CMD,cmd,getRedirect()));					
			}else{
				cmds.add(String.format("echo '%s' | %s %s %s",sudoPassword,SUDO_CMD,cmd,getRedirect()));
			}					
		}
		return this;
	}
	/**
	 * 添加一条普通命令
	 * @param cmd
	 * @return
	 */
	public CmdExecutor cmd(String cmd){
		if(null != cmd && 0 != cmd.length()){
			cmds.add(String.format("%s %s",cmd,getRedirect()));					
		}
		return this;
	}
	private List<String> build(){
		return cmds.isEmpty()
				? Collections.<String>emptyList()
				: Arrays.asList(SHELL_NAME,SHELL_PARAM,join(cmds,cmdSeparator));
	}
    private static String join(List<String> strs,String separator) {
        StringBuffer buffer = new StringBuffer();
        for(int i=0;i<strs.size();++i){
        	if(i>0){
        		buffer.append(separator);
        	}
            buffer.append(strs.get(i));
        }
        return buffer.toString();
    }
    /**
     * 将{@link InputStream}中所有内容输出到{@link StringBuffer}
     * @param in
     * @return
     * @throws IOException
     */
    private static void toBuffer(InputStream in,StringBuffer buffer) throws IOException{
    	if(null == in || null == buffer){
    		return ;
    	}
		InputStreamReader ir = new InputStreamReader(in);
		LineNumberReader input = new LineNumberReader(ir);
		try{
			String line;
			while ((line = input.readLine()) != null) {
				buffer.append(line).append("\n");
			}
		}finally{
			input.close();
		}
    }
    /** 
     * 调用{@link Runtime#exec(String[])}执行命令 
     * @return 返回输出结果 
     */
	public String exec() throws IOException{
		StringBuffer outBuffer = new StringBuffer();
		exec(outBuffer,null);
		return outBuffer.toString();
	}
	/**
	 * 调用{@link Runtime#exec(String[])}执行命令 
	 * @param outBuffer 标准输出
	 * @param errBuffer 错误信息输出
	 * @throws IOException
	 */
	public void exec(StringBuffer outBuffer,StringBuffer errBuffer) throws IOException{
		List<String> cmdlist = build();
		if(!cmdlist.isEmpty()){
			if(verbose){
				logger.info(join(cmdlist," "));
			}
			Process process = Runtime.getRuntime().exec(cmdlist.toArray(new String[cmdlist.size()]));
			if(sync){
				try {
					process.waitFor();
				} catch (InterruptedException e) {
				}
			}
			toBuffer(process.getInputStream(),outBuffer);
			toBuffer(process.getErrorStream(),errBuffer);
		}
	}
}

使用示例如下:

import static org.junit.Assert.*;

public class CmdExecutorTest {

	@Test
	public void test() {
		try {
			// 创建一个CmdExecutor实例,通过sudoCmd或cmd添加要执行的命令,最后调用exec执行。
			String out = CmdExecutor.builder("sudopassword")
			.errRedirect(false)
			.sudoCmd("date -s '2016-12-27 23:23:23'")// 修改系统时间
			.sudoCmd("clock -w")
			.sudoCmd("cat /etc/sysconfig/network")
			.cmd("date")
			.sudoCmd("ntpdate -u time.windows.com")// 系统时间同步
			.exec();
			System.out.println(out);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

代码仓库地址:
https://gitee.com/l0km/common-java/blob/master/common-base/src/test/java/net/gdface/common/CmdExecutorTest.java

参考资料

《解决sudo: sorry, you must have a tty to run sudo》http://blog.csdn.net/breezehappy/article/details/38895375

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10km

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值