I'm writing a java tool that needs to retrieve output returned by diskpart. Diskpart is called using the /s option with a specified script. I'm using Windows 7 with the lowest possible UAC settings.
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "diskpart", "/s", "c:\\dps.txt");
Process p = builder.start();
p.waitFor();
InputStream ins = p.getInputStream();
System.out.println(ins.available()); // Output: 0
Using the following line instead of the above produces an empty output file c:\dps_out.txt
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "diskpart", "/s", "c:\\dps.txt", ">>", "c:\\dps_out.txt" );
When calling diskpart using this method, there seems to be nothing to read from standard output, since Process.getInputStream() and the Windows output redirection both access standard output and fail to read data.
Calling diskpart directly requires elevation.
ProcessBuilder builder = new ProcessBuilder("diskpart", "/s", "c:\\dps", ">>", "c:\\dps_out" );
Exception in thread "main" java.io.IOException: Cannot run program "diskpart": CreateProcess error=740, The requested operation requires elevation.
How do I properly run the diskpart script and read its output from within the java tool?
What is your question?
This is from DiskPart's manifest.
<requestedPrivileges>
<requestedExecutionLevel
level="requireAdministrator"
uiAccess="false"
/>
</requestedPrivileges>
Cmd doesn't require admin access so works (it's set AsInvoker). Calling DiskPart direct won't work due to the manifest - requireAdministrator.
Related
I'm trying to use Java's ProcessBuilder class to execute a command that has a pipe in it. For example:
ls -l | grep foo
However, I get an error:
ls: |: no such file or directory
Followed by:
ls: grep: no such file or directory
Even though that command works perfectly from the command line, I can not get ProcessBuilder to execute a command that redirects its output to another.
Is there any way to accomplish this?
This should work:
ProcessBuilder b = new ProcessBuilder("/bin/sh", "-c", "ls -l| grep foo");
To execute a pipeline, you have to invoke a shell, and then run your commands inside that shell.
The simplest way is to invoke the shell with the command line as the parameter. After all, it's the shell which is interpreting "|" to mean "pipe the data between two processes".
Alternatively, you could launch each process separately, and read from the standard output of "ls -l", writing the data to the standard input of "grep" in your example.
Since Java 9, there’s genuine support for piplines in ProcessBuilder.
So you can use
List<String> result;
List<Process> processes = ProcessBuilder.startPipeline(List.of(
new ProcessBuilder("ls", "-l")
.inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE),
new ProcessBuilder("grep", "foo")
.redirectError(ProcessBuilder.Redirect.INHERIT)
));
try(Scanner s = new Scanner(processes.get(processes.size() - 1).getInputStream())) {
result = s.useDelimiter("\\R").tokens().toList();
}
to get the matching lines in a list.
Or, for Windows
List<String> result;
List<Process> processes = ProcessBuilder.startPipeline(List.of(
new ProcessBuilder("cmd", "/c", "dir")
.inheritIO().redirectOutput(ProcessBuilder.Redirect.PIPE),
new ProcessBuilder("find", "\"foo\"")
.redirectError(ProcessBuilder.Redirect.INHERIT)
));
try(Scanner s = new Scanner(processes.get(processes.size() - 1).getInputStream())) {
result = s.useDelimiter("\\R").tokens().toList();
}
These examples redirect stdin of the first process and all error streams to inherit, to use the same as the Java process.
You can also call .redirectOutput(ProcessBuilder.Redirect.INHERIT) on the ProcessBuilder of the last process, to print the results directly to the console (or wherever stdout has been redirected to).
I am currently trying to write a small program in java which should take over the job of an old batch script I've been using.
This batch script executes a program called sum.exe (Supermicro Update Manager).
However, no matter which way I try, the program either does not respond, or straight up tells me it can't find the file in the directory where the file is.
boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");
ProcessBuilder builder = new ProcessBuilder("C:\\Users\\[Username]\\SUM\\sum.exe");
if (isWindows) {
builder.command("sum.exe", "-i 192.168.4.10 -u ADMIN -p ADMIN -c CheckOOBSupport");
} else {
builder.command("sh", "-c", "ls");
}
builder.redirectErrorStream(true);
Process process = builder.start();
StreamGobbler streamGobbler = new StreamGobbler(process.getInputStream(), System.out::println);
StreamGobbler streamGobblerErrors = new StreamGobbler(process.getErrorStream(), System.out::println);
Executors.newSingleThreadExecutor().submit(streamGobbler);
Executors.newSingleThreadExecutor().submit(streamGobblerErrors);
int exitCode = process.waitFor();
assert exitCode == 0;
This is the code I currently have. The command I'm trying to call here will 100% give an error, so I made sure to redirect those as well.
As far as I understood, there are 3 different ways to set a Filepath for the Processbuilder.
Either you:
Set the path in the constructor
Set the path between your executable and arguments in the .command() method
Or you set the directory of the builder by creating a new file (and using System.Property)
I have a complete copy of the SUM-Folder under: C:\Users\[Username]\SUM, and I have tried all 3 options listed above with this, but always got the error message that the system could not find the file specified
Additionally, I'm still not sure if the command would even work this way. I have only ever used sum.exe via batch-Script or cmd.exe itself, so wouldn't the command need to be
builder.command("cmd.exe", "sum.exe -i 192.168.4.10 -u ADMIN -p ADMIN -c CheckOOBSupport)
instead?
Can anyone tell me what I'm doing wrong?
Thanks!
The ProcessBuilder command line is passed in the constructor or the command() method so in your example you've overridden the value used.
Choose the way you need:
ProcessBuilder builder = new ProcessBuilder("C:\\Users\\[Username]\\SUM\\sum.exe",
"-i", "192.168.4.10",
"-u", "ADMIN","-p", "ADMIN",
"-c", "CheckOOBSupport");
or
ProcessBuilder builder = new ProcessBuilder();
builder.command("sum.exe",
"-i", "192.168.4.10",
"-u", "ADMIN","-p", "ADMIN",
"-c", "CheckOOBSupport");
Note also that the arguments for the command need to supplied as separate string values rather than all concatenated together as one value, and you need absolute path to "sum.exe" if that is not found in the current directory or under a directory of environment variable "Path".
So guys I want to execute a command that you can execute on the cmd in my Java program. After doing some study, I thought i found a way to do this. However, my code doesn't work.
My code is
import java.io.*;
public class CmdTest {
public static void main(String[] args) throws Exception {
String[] command = {"ag","startTimes conf.js >> pro.txt"};
ProcessBuilder builder = new ProcessBuilder(command);
builder.directory(new File("./test-java/"));
Process p = builder.start();
}
}
The program executes but produces no output. I tried using other commands like "ls -a", but still no output.
Can someone please help me debug this or suggest a better way of doing this? Thank you
Edit 1: I am executing this on a Mac. If that is necessary for debugging
Edit 2: The usual ls and other commands are working with the solutions that you guys have provided. I however want to use the ag (the_silver_searcher) command in the Java program. When i try that, i get the following error -
Exception in thread "main" java.io.IOException: Cannot run program "ag startTimes conf.js >> pro.txt": error=2, No such file or directory
The existing answers give you the information on how to solve your problem in code, but they don't give a reason why your code is not working.
When you execute a program on a shell, there's significant processing done by the shell, before the program is ever executed. Your command line
String[] command = {"ag","startTimes conf.js >> pro.txt"};
ProcessBuilder builder = new ProcessBuilder(command);
assumes that the command ag is run with the single argument startTimes conf.js >> pro.txt - most likely not what you want to do. Let's go one step further: What if you wrote
String[] command = {"ag","startTimes", "conf.js", ">>", "pro.txt"};
ProcessBuilder builder = new ProcessBuilder(command);
?
This would assume that the ag command knows about the >> parameter to redirect its output - and here is where the shell comes into play: The >> operator is an instruction to the shell, telling it what to do with the output from stdout of the process. The process ag, when started by the shell, never has an idea of this redirection and has no clue about >> and the target file name at all.
With this information, just use the code samples from any of the other answers. I won't copy them into mine for proper attribution.
While there is ProcessBuilder, I've always used Runtime.getRuntime().exec("cmd");
Process Runtime.exec(String)
It returns a Process which you can get the input and output streams of
Even if you stay with the ProcessBuilder, you should still have access to the Process.get<Input/Output/Error>Stream()
You need to read the output of the process by opening an input stream from the process:
try (BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream())) {
System.out.println(reader.readLine()); // process the output stream somehow
}
Additionally you might the read the error stream ( p.getErrorStream()), which I often have done in a separate stream, in Java 8 you can use redirectErrorStream(true) on the ProcessBuilder to automatically add the error stream to the input stream. Of course you can't distinquish anymore from which stream the input comes, but it makes reading easier. If you don't read the input or error stream and the process's buffer becomes full the processes tend to pause until there is enough room in the buffer again.
You can also add
builder.redirectOutput(ProcessBuilder.Redirect.INHERIT);
before the start method which redirects the output to the console.
//"ls" command runs under the "sh" on linux(cmd.exe on windows), so first arg is "sh"
//second arg "-c" tells "sh" which exact command should be executed
//"ls" is actual command
//"startTimes" as I understand is a file or directory, it is arg for "ls" command
//"conf.js" is second arg for "ls" command
new ProcessBuilder("sh", "-c", "ls", "startTimes", "conf.js")
//set working dir for "sh" process"
.directory(new File("./test-java/"))
//output will be written to "pro.txt" in working dir of "sh" process
.redirectOutput(new File("./test-java/pro.txt"))
.start();
I have exe file which needs admin right to be run. I am using ProcessBuilder this way:
ProcessBuilder processBuilder = new ProcessBuilder("cmd.exe", "/C", "start", "/wait", "myexe.exe", "&", "echo", "%errorlevel%");
Process p = processBuilder.start();
As part of ProcessBuilder constructor parameters there has to be cmd.exe /C because without it it is impossible to starts myexe.exe - I always get error message
The requested operation requires elevation.
With cmd.exe /C it is started normally and UAC asked me if I am sure to start it. But then I got to
java %errorlevel% from cmd.exe (process.exitValue())
which is always 0 and not %errorlevel% from myexe.exe.
Then I try to concatenate commands by & end echo %errorlevel%. Then I open input stream and read what was written by echo. It always read 0.
It works in batch file:
myexe.exe
echo %errorlevel&
But not inline as for ProcessBuilder. Any help?
To get the "errorlevel" (really the process exit value), you can use something like
Process p = processBuilder.start();
int errorLevel = p.waitFor();
The Process.waitFor() Javadoc says (in part),
Returns:
the exit value of the subprocess represented by this Process object. By convention, the value 0 indicates normal termination.
I am using Windows!
I want to call a small .exe application from my java command line which is called "saucy.exe". It needs an input file "input.saucy". Both are stored in the correct directory.
When I use the command
Process p = Runtime.getRuntime().exec("saucy input.saucy");
everything works fine and I get an output on the console.
However, when I try to write the output in a file
Process p = Runtime.getRuntime().exec("saucy input.saucy > output.saucy");
nothing happens.
I already found the advice in http://www.ensta-paristech.fr/~diam/java/online/io/javazine.html and tried to tokenize the command manually:
String[] cmd = {"saucy", "input.saucy > output.saucy"};
Process p = Runtime.getRuntime().exec(cmd);
It is still not working. Any advice? It is no option for me to write the output to a file with java code, because its too slow.
Again: I am using Windows (I stress that because I read several hints for Linux systems).
> is a shell command, but you are not using one. try
String[] cmd = { "cmd", "/C", "saucy input.saucy > output.saucy" };
If you are on Java 7 you can use the new ProcessBuilder.redirectOutput mechanism:
ProcessBuilder pb = new ProcessBuilder("saucy", "input.saucy");
// send standard output to a file
pb.redirectOutput(new File("output.saucy"));
// merge standard error with standard output
pb.redirectErrorStream(true);
Process p = pb.start();
Use the getInputStream(), getOutputStream() and getErrorStream() to retrieve the output (or send input).
http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/Process.html