I'm trying to figure out how to run a PowerShell script from within Java. Please keep in mind that I'm very new to Java, so there might be a better way of doing this.
Currently I'm on a Fedora 25 workstation where I installed PowerShell.
As explained here, fist install the Fedora .NET Core package:
sudo dnf config-manager --add-repo https://copr.fedorainfracloud.org/coprs/nmilosev/dotnet-sig/repo/fedora-25/nmilosev-dotnet-sig-fedora-25.repo
sudo dnf update
sudo dnf install dotnetcore
Then download the RPM for CentOS 7 and install it.
sudo dnf install Downloads/powershell-6.0.0_beta.1-1.el7.centos.x86_64.rpm
powershell
Then I used the code from this post to run a "Hello world" ps1 file:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class PowerShellCommand {
public static void main(String[] args) throws IOException {
String command = "powershell $PSVersionTable.PSVersion";
command = "powershell -ExecutionPolicy RemoteSigned -NoProfile -NonInteractive -File \"/home/b/Downloads/MyScript.ps1\"";
Process powerShellProcess = Runtime.getRuntime().exec(command);
powerShellProcess.getOutputStream().close();
String line;
System.out.println("Standard Output:");
BufferedReader stdout = new BufferedReader(new InputStreamReader(
powerShellProcess.getInputStream()));
while ((line = stdout.readLine()) != null) {
System.out.println(line);
}
stdout.close();
System.out.println("Standard Error:");
BufferedReader stderr = new BufferedReader(new InputStreamReader(
powerShellProcess.getErrorStream()));
while ((line = stderr.readLine()) != null) {
System.out.println(line);
}
stderr.close();
System.out.println("Done");
}
}
The error I'm getting when trying to run the .ps1 file is:
Processing -File '"/home/b/Downloads/MyScript.ps1"' failed because the file does not have a '.ps1' extension. Specify a valid Windows PowerShell script file name, and then try again.
When trying to run this from within the Java code I do get the correct output for the variable:
String command = "powershell $PSVersionTable.PSVersion";
When running the following from the bash shell, in a Gnome terminal, this works fine too and the script gets executed properly by saying "Hello world":
powershell -ExecutionPolicy RemoteSigned -NoProfile -NonInteractive -File "/home/b/Downloads/MyScript.ps1"
Thank you for your help.
Note:
Since the question was posted, the name of the PowerShell Core executable has changed from powershell
to pwsh
, which is reflected in the solutions below.
The specific script-file path in the question - /home/b/Downloads/MyScript.ps1
- does not strictly require quoting (being enclosed in "..."
), but a generally robust solution should use quoting - that is what is demonstrated below.
By constructing your command as a single string, the embedded "
instances are retained as literals, which is what the error message reflects:
Processing -File
'"/home/b/Downloads/MyScript.ps1"'
...
Note how the quoted value inside '...'
includes enclosing "..."
- and a path that literally contains "
characters obviously doesn't exist and - due to ending in "
- doesn't have a .ps1
extension.
What the single-command-string form of Runtime.exec()
does is to perform by-whitespace-only tokenizing of the string via StringTokenizer
. The resulting array therefore retains embedded quotes and also doesn't recognize quoted tokens with embedded whitespace as single tokens.
You have two choices for solving this problem:
(a) Use -Command
and &
instead of -File
to invoke the script, in which case PowerShell employs another round of parsing, and the embedded "
instances are therefore recognized as having syntactic function.
String command = "pwsh -NoProfile -Command & \"/home/b/Downloads/MyScript.ps1\"";
(b) Preferably, pass the tokens of the command line as a string array:
String[] command = new String[] { "pwsh", "-NoProfile", "-File", "/home/b/Downloads/MyScript.ps1" };
Note that with (a) if you include arguments to pass to the script in the command string, these arguments too are parsed by PowerShell, which means you may need embedded quoting for them too.
Note how with (b) no embedded quoting is necessary for the script-path argument, given that it is specified as its own array element. By using PowerShell's -File
parameter, all arguments passed are treated as literals.