You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

162 lines
4.4 KiB
Java

package com.baiye.system;
import com.baiye.util.FileUtils;
import com.baiye.exception.CommandTimeoutException;
import com.baiye.util.SystemUtils;
import org.springframework.util.StringUtils;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.time.LocalDateTime;
import java.util.StringTokenizer;
import java.util.concurrent.TimeUnit;
/**
* @author lingting 2022/6/25 11:55
*/
public class Command {
public static final String NEXT_LINE = SystemUtils.lineSeparator();
public static final String EXIT_COMMAND = "exit";
private final Process process;
private final OutputStream stdIn;
/**
*
*/
private final File stdOut;
private final File stdErr;
private final String nextLine;
private final String exit;
private final Charset charset;
private final LocalDateTime startTime;
private Command(String init, String nextLine, String exit, Charset charset) throws IOException {
if (!StringUtils.hasText(init)) {
throw new IllegalArgumentException("Empty init");
}
StringTokenizer st = new StringTokenizer(init);
String[] cmdArray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++) {
cmdArray[i] = st.nextToken();
}
this.stdOut = FileUtils.createTemp();
this.stdErr = FileUtils.createTemp();
// 重定向标准输出和标准错误到文件, 避免写入到缓冲区然后占满导致 waitFor 死锁
ProcessBuilder builder = new ProcessBuilder(cmdArray).redirectError(stdErr).redirectOutput(stdOut);
this.process = builder.start();
this.stdIn = process.getOutputStream();
this.nextLine = nextLine;
this.exit = exit;
this.charset = charset;
this.startTime = LocalDateTime.now();
}
/**
* . 使, , 使
* {@link Command#of(String, Charset)}
* @param init
*/
public static Command of(String init) throws IOException {
return of(init, SystemUtils.charset());
}
/**
* 使
*/
public static Command of(String init, Charset charset) throws IOException {
return of(init, NEXT_LINE, EXIT_COMMAND, charset);
}
public static Command of(String init, String nextLine, String exit, Charset charset) throws IOException {
return new Command(init, nextLine, exit, charset);
}
public Command write(String str) throws IOException {
stdIn.write(str.getBytes(charset));
stdIn.flush();
return this;
}
/**
*
*/
public Command line() throws IOException {
return write(nextLine);
}
/**
* 退
*/
public Command exit() throws IOException {
write(exit);
return line();
}
/**
*
* @param str
*/
public Command exec(String str) throws IOException {
write(str);
return line();
}
/**
* , 退
* <p>
* : , 退
* </p>
* <p>
* : eg: exec("ssh ssh.lingting.live").exec("ssh ssh.lingting.live").exec("ssh
* ssh.lingting.live")
* </p>
* <p>
* : eg: exit().exit().exit()
* </p>
*/
public CommandResult result() throws InterruptedException {
process.waitFor();
return CommandResult.of(stdOut, stdErr, startTime, LocalDateTime.now(), charset);
}
/**
*
* <h3> process {@link Runtime#exec}, {@link Process#waitFor}线,
* </h3>
* <p>
* , , 线,, . 线waitFor()线,
* </p>
* <p>
* 便. : , , .
* </p>
* @param millis , :
* @return live.lingting.tools.system.CommandResult
*/
public CommandResult result(long millis) throws InterruptedException, CommandTimeoutException {
if (process.waitFor(millis, TimeUnit.MILLISECONDS)) {
return result();
}
// 超时. 强行杀死子线程
process.destroyForcibly();
throw new CommandTimeoutException();
}
public void close() {
process.destroy();
}
}