在某些场景下,需要在Java程序中使用Powershell进行终端交互,这种情况下当然可以直接使用自带的Runtime来完成:
Runtime.getRuntime().exec("powershell.exe Get-Item");
但是这种只适合需要单条指令的情况,而存在多条指令时,无法保证前后指令的关联性。这里介绍一个第三方的类库:jPowershell
Maven中引入如下依赖:
<dependency>
<groupId>com.profesorfalken</groupId>
<artifactId>jPowerShell</artifactId>
<version>3.1.1</version>
</dependency>
接下来,在程序中就可以通过如下方式获取到一个Powershell的实例:
PowerShell session = PowerShell.openSession();
PowerShell对象提供了如下的基础方法:
| 指定配置对象 | void | 修改默认配置时使用 |
| | PowerShell | 启动一个PowerShell会话 |
| 指定PowerShell终端的路径 | PowerShell | 启动一个PowerShell会话 |
| Powershell指令 | PowerShellResponse | 执行一条PowerShell指令 |
| Powershell指令 | PowerShellResponse | 执行单一PowerShell指令(无会话模式) |
| PowerShell指令,Response处理器 | PowerShell | 执行一条PowerShell指令并对其Response进行处理 |
| Response处理器,Response | void | 使用指定Response处理器处理响应 |
| | boolean | 最后一条指令是否出错 |
| 脚本路径 | PowerShellResponse | 执行指定的脚本 |
executeScript(String arg0, String arg1) | 脚本路径,执行参数 | PowerShellResponse | 传入参数执行指定脚本 |
executeScript(BufferedReader arg0) | 脚本缓冲对象 | PowerShellResponse | 从缓冲对象中执行脚本 |
executeScript(BufferedReader arg0, String arg1) | 脚本缓冲对象,执行参数 | PowerShellResponse | 传入参数执行缓冲对象中的脚本 |
close() | | void | 关闭会话 |
closeAndWait(Future<String> arg0) | 异步对象列表 | boolean | 等待异步对象列表中全部Task完成后关闭会话 |
checkState() | | void | 检查当前会话状态 |
获取到的session对象是一个新的Powershell会话实例,可以通过下述方式来执行指令:
String readContext = "Get-Content D:\\1.sql";
session.executeCommand("$user = \""+username+"\"");
session.executeCommand("$password = ConvertTo-SecureString \"" +password + "\" -AsPlainText -Force");
session.executeCommand("$credential = New-Object System.Management.Automation.PSCredential($user,$password)");
response = session.executeCommand("Invoke-Command -ComputerName " + server + " -Credential $credential -ScriptBlock {" + readContext + "}");
System.out.println(response.getCommandOutput().length());
上述代码中,首先定义了两个shell变量,$user和$password,而后通过调用System.Management.Automation.PSCredential构造了一个$credential对象,再将其作为-Credential的值传入下一条指令中,完成远程Invoke-Command进行文件内容长度的读取。
每一条session.executeCommand()都会返回一个PowerShellResponse对象,可以通过调用这个对象的getCommandOutput()方法来获取命令输出。注意看日志最后的数字:
当然,在某些情况下,调用的指令执行时间会很长,在默认情况下,Powershell实例只会等待10秒钟,如果10秒钟之后还没有返回值,就直接跳到下一条继续执行了。这种情况下,可以通过配置Powershell的实例来修改等待时间。来看源码:
public class PowerShell implements AutoCloseable {
private static final Logger logger = Logger.getLogger(PowerShell.class.getName());
private Process p;
private long pid = -1L;
private PrintWriter commandWriter;
private boolean closed = false;
private ExecutorService threadpool;
private static final String DEFAULT_WIN_EXECUTABLE = "powershell.exe";
private static final String DEFAULT_LINUX_EXECUTABLE = "powershell";
private int waitPause = 5;
private long maxWait = 10000L;
private File tempFolder = null;
private boolean scriptMode = false;
public static final String END_SCRIPT_STRING = "--END-JPOWERSHELL-SCRIPT--";
private PowerShell() {
}
public PowerShell configuration(Map<String, String> config) {
try {
this.waitPause = Integer.valueOf(config != null && config.get("waitPause") != null ? (String)config.get("waitPause") : PowerShellConfig.getConfig().getProperty("waitPause"));
this.maxWait = Long.valueOf(config != null && config.get("maxWait") != null ? (String)config.get("maxWait") : PowerShellConfig.getConfig().getProperty("maxWait"));
this.tempFolder = config != null && config.get("tempFolder") != null ? this.getTempFolder((String)config.get("tempFolder")) : this.getTempFolder(PowerShellConfig.getConfig().getProperty("tempFolder"));
} catch (NumberFormatException var3) {
logger.log(Level.SEVERE, "Could not read configuration. Using default values.", var3);
}
return this;
}
}
Powershell提供了一个configuration()方法,这个方法接受一个Map类型的配置参数,并将内容解析后代替默认参数。我们主要关注的是maxWait的值。通过成员定义可以看出,默认的maxWait的值就是10000毫秒,也就是10秒。因此,可以通过如下代码来使其支持更长时间指令的调用:
PowerShell session = PowerShell.openSession();
Map<String,String> configMap = new HashMap<>();
configMap.put("maxWait","600000");
session.configuration(configMap);
这样就把指令的最长等待时间设置为了600秒。