ProcessBuilder调用PowerShell时引号丢失致命令执行异常如何解决?
你的问题很典型——当用Java的ProcessBuilder调用PowerShell执行带引号的WQL查询时,引号被意外移除,导致PowerShell把查询语句拆成了多个无效的位置参数,从而抛出PositionalParameterNotFound错误。
问题根源
当你直接把完整的命令字符串作为第二个参数传给powershell.exe时,PowerShell会自动移除命令字符串外层的双引号,导致原本应该作为-Query参数值的"SELECT * FROM Win32_Printer WHERE Default=$true"变成了SELECT * FROM Win32_Printer WHERE Default=$true。这时候PowerShell会把SELECT当作-Query的参数,然后*和后续内容被当作多余的位置参数,自然就报错了。
修复方法
这里有两种可靠的解决方式,你可以根据需求选择:
方法一:用-Command参数+正确转义引号和特殊字符
明确使用-Command参数告诉PowerShell执行后续的命令字符串,同时转义好双引号和PowerShell的特殊字符(比如$):
public static String execute(String cmd) { System.out.println(cmd); // 注意这里的参数拆分和转义 ProcessBuilder builder = new ProcessBuilder( "powershell.exe", "-Command", cmd.replace("$", "`$") // 转义PowerShell中的$符号 ); StringBuilder fullStatus = new StringBuilder(); String line = null; Process reg; builder.redirectErrorStream(true); try { reg = builder.start(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(reg.getInputStream())); while((line = bufferedReader.readLine()) != null) { fullStatus.append(line).append("\n"); } reg.waitFor(); // 建议用waitFor代替destroy,确保命令执行完成 reg.destroy(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } return fullStatus.toString(); }
调用时,你的命令字符串需要保留双引号(Java中要转义):
String result = execute("Get-WmiObject -Query \"SELECT * FROM Win32_Printer WHERE Default=$true\"");
这里的关键是:
- 添加
-Command参数明确命令模式 - 把
$转义成$,避免PowerShell将$true当作变量解析 - Java字符串中的双引号用
\"转义,确保传递给PowerShell时保留引号
方法二:拆分命令为独立参数(更推荐)
把命令的每个部分作为单独的参数传递给ProcessBuilder,这样PowerShell会自动识别每个参数的边界,不需要手动处理引号转义:
public static String executePrinterQuery() { ProcessBuilder builder = new ProcessBuilder( "powershell.exe", "-Command", "Get-WmiObject", "-Query", "SELECT * FROM Win32_Printer WHERE Default=$true" ); StringBuilder fullStatus = new StringBuilder(); String line = null; Process reg; builder.redirectErrorStream(true); try { reg = builder.start(); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(reg.getInputStream())); while((line = bufferedReader.readLine()) != null) { fullStatus.append(line).append("\n"); } reg.waitFor(); reg.destroy(); } catch (IOException | InterruptedException e) { e.printStackTrace(); } return fullStatus.toString(); }
这种方式更清晰,也避免了引号转义的各种坑,ProcessBuilder会帮你正确处理参数的传递。
额外建议
另外,微软已经推荐使用Get-CimInstance代替老旧的Get-WmiObject,你可以替换命令获得更好的兼容性:
ProcessBuilder builder = new ProcessBuilder( "powershell.exe", "-Command", "Get-CimInstance", "-Query", "SELECT * FROM Win32_Printer WHERE Default=$true" );
内容的提问来源于stack exchange,提问作者Sebastian G.

