java:执行linux sudo命令
我们知道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类,允许执行多条命令。
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();
}
}
}
参考资料
《解决sudo: sorry, you must have a tty to run sudo》http://blog.csdn.net/breezehappy/article/details/38895375
下一篇: OC重新开始(八)属性