is there a limit of commands on a ProcessBuilder?
I have this array of commands:
protected String[] cmd = {
"dism /mount-wim /wimfile:boot.wim /index:2 /mountdir:mount",
"dism /image:mount /add-driver:\"driver\" /recurse",
"dism /unmount-wim /mountdir:mount /commit",
"dism /mount-wim /wimfile:install.wim /index:" + formPanel.getOsIndex() + " /mountdir:mount"
};
And this is my ProcessBuilder:
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe", "/c", cmd[0] + " && " + cmd[1] + " && " + cmd[2] + " && " + cmd[3] + " && " + cmd[1] + " && " + cmd[2]
);
But when I run it it says '&& was unexpected at this time'. When I change the processbuilder to this:
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe", "/c", cmd[0] + " && " + cmd[1] + " && " + cmd[2]
);
Then it works fine.
So my question is basically just if there's a sort of limit of how many commands a single processbuilder can pass?
Here's the whole segment of my SwingWorker method:
#Override
protected Integer doInBackground() {
try {
ProcessBuilder pb = new ProcessBuilder(
"cmd.exe", "/c", cmd[0] + " && " + cmd[1] + " && " + cmd[2] + " && " + cmd[3] + " && " + cmd[1] + " && " + cmd[2]
);
pb.directory(new File(formPanel.workspaceDir.toString()));
pb.redirectErrorStream(true);
Process p = pb.start();
String s;
BufferedReader stdout = new BufferedReader(new InputStreamReader(p.getInputStream()));
while((s = stdout.readLine()) != null && !isCancelled()) {
publish(s);
System.err.println(s);
}
if(!isCancelled()) {
status = p.waitFor();
}
p.getInputStream().close();
p.getOutputStream().close();
p.getErrorStream().close();
p.destroy();
} catch(IOException | InterruptedException ex) {
ex.printStackTrace(System.err);
}
return status;
}
I'm starting to wonder if there's something wrong with the actual code, not the commands.
I think the limit you have to take into account first is the limit of a command itselft (then ProcessBuilder) which is different if you're on Windows or Unix.
For Windows, according to "Command prompt (Cmd. exe) command-line string limitation" documentation :
On computers running Microsoft Windows XP or later, the maximum length
of the string that you can use at the command prompt is 8191
characters. On computers running Microsoft Windows 2000 or Windows NT
4.0, the maximum length of the string that you can use at the command prompt is 2047 characters.
This limitation applies to the command line, individual environment
variables (such as the PATH variable) that are inherited by other
processes, and all environment variable expansions. If you use Command
Prompt to run batch files, this limitation also applies to batch file
processing.
For Unix, I suggest you to refer to the following Stackoverflow question which is resolved now :
Maximum number of Bash arguments != max num cp arguments?
Also, you should take account of limit size of an array in Java which is described into the following Stackoverflow question :
Do Java arrays have a maximum size?
I think the whole command that you sent might be too long for cmd.exe as you use the executable there did you considered using Runtime.exec(); or something like this ?
List<String> commands = new ArrayList<>();
final ProcessBuilder builder = new ProcessBuilder();
commands.add("dism /mount-wim /wimfile:boot.wim /index:2 /mountdir:mount");
And so on, additionaly im not sure if you can have whitespaces here, or you need to add everything as a seperate command.
builder.command(commands);
builder.directory(new File(workingDir));
process = builder.start();
Related
I have a simple GUI which selects an executable and a batch file. Clicking "run" should launch a command line instance, then run the executable given the selected batch. However, hiccups seem to show up at different points. This is the relevant code snippet:
String[] commands = {"cmd.exe", "/c", "C:\\Xilinx\\14.7\\ISE_DS\\settings64.bat && cd /d ",
"\"" + simFile.getParent() + "\"", " && ping localhost && ",
"\"" + jTextField1.getText() + "\"", " -tclbatch \"" + jTextField2.getText() + "\""};
ProcessBuilder simBuilder = new ProcessBuilder(commands);
simBuilder.redirectErrorStream(true);
Process simulation = simBuilder.start();
BufferedReader reader = new BufferedReader(new InputStreamReader(simulation.getInputStream()));
String line;
while (true) {
line = reader.readLine();
if (line == null)
break;
System.out.println(line);
}
I chose to create a process through a ProcessBuilder rather than "Runtime.getRuntime().exec" because having the command and arguments as a String array is more readable and manageable. I took a look through the documentation of Runtime, Process, and ProcessBuilder. I also searched for similar questions, the following being the closest: Run cmd commands through Java. However, I'm still having issues getting all commands to run properly, if it all. First point: The program successfully executes the commands until "ping", which I placed to determine where the issue occurs. I get the cmd output in the console through the BufferedReader just fine. However, the next command, which should run the executable indicated by "jTextField1.getText()", gives an error of "The filename, directory name, or volume label syntax is incorrect" although I made sure the path is within escaped double quotes to account for spaces. Is it something in my syntax? Something to do with where the double ampersands are placed? Does every separate command with its argument need to be its own string in the array? I tried that and different things, but it always seems to result in an error.
You should check that your path names are correct, and try the cmd as one parameter value not as comma separated after cmd.exe /c. This will ensure the arguments are passed to CMD correctly as a single argument for the CMD shell to handle:
import java.nio.file.Files
System.out.println("Files.isDirectory(simFile.getParent())="+Files.isDirectory(simFile.getParent()));
System.out.println("Files.isExecutable(jTextField1.getText())="+Files.isExecutable(Path.of(jTextField1.getText())));
String cmd = "C:\\Xilinx\\14.7\\ISE_DS\\settings64.bat && cd /d "+
"\"" + simFile.getParent() + "\" && ping localhost && "+
"\"" + jTextField1.getText() + "\" -tclbatch \"" + jTextField2.getText() + "\"";
String[] commands = {"cmd.exe", "/c", cmd};
I have written a JavaFX program that runs a python script that uses google console search api to get data. It works fine before I deploy it as a standalone program (.exe). After I install the program it runs fine until I click the button that runs the script; it does nothing.
After the button is clicked it succesfully checks if the python is installed or not, after that the problem occurs when it tries the run the script. I have specified the path like this, "./src/com/fortunamaritime/" and I think this might be the problem.
private static final String ANALYTICS_PATH ="./src/com/fortunamaritime/";
inside the method
//set up the command and parameter
String[] cmd = new String[3];
cmd[0] = "python";
cmd[1] = "analytics.py";
cmd[2] = "https://fortunamaritime.com.tr";
String command = cmd[0] + " " + cmd[1] + " " + cmd[2] + " " +
startDate + " " + endDate;
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "cd " + new
File(ANALYTICS_PATH).getAbsolutePath() + " && " + command);
builder.redirectErrorStream(true);
Process p = builder.start();
I have redirected console output to a .txt file to see what was happening and there's only one line that said "system cannot find the path specified".
I have switched from absolute path to relative path and it produced this error:
The filename, directory name, or volume label syntax is incorrect.
I also printed this line to console
this.getClass().getResource("/com/fortunamaritime/analytics.py")
and the result is:
jar:file:/D:/IdeaProjects/FortunaWebTracker/out/artifacts/FortunaWebTracker/FortunaWebTracker.jar!/com/fortunamaritime/analytics.py
Is it possible to access the inside of the jar file and run the script from there via cmd through stripping parts of this URL?
Copy the script to a (maybe temporary) file and run it from that file.
You can get the script as InputStream with Main.class.getResourceAsStream(String resc) (relative to main) or Main.class.getClassLoader().getResourceAsStream(String resc) (relative to root)
You may use java.nio.file.Files#createTempFile(String,String and delete that file on exit.
You can also copy the script into the user home or relative to the exe (and don't delete it).
For example, you could do this:
File dir=new File("./resc");
if(!dir.exists()) {
dir.mkdirs();
}
File analyticsFile=new File(dir,"analytics.py");
if(!analyticsFile.exists()) {
/*
* alternative:
* this.getClass().getResourceAsStream("com/fortunamaritime/analytics.py")
*/
try(InputStream analyticsIn = this.getClass().getClassLoader().getResourceAsStream("analytics.py")){
Files.copy(analyticsIn, analyticsFile.toPath());
}
}
/*
* alternative: String command=String.join(" ",new String[]{"python",analyticsFile.getAbsolutePath(),"https://fortunamaritime.com.tr"});
*/
String command="python "+analyticsFile.getAbsolutePath()+" https://fortunamaritime.com.tr";
/*
* alternative (without error redirection): Process p=Runtime.getRuntime().exec(command);
*/
ProcessBuilder builder = new ProcessBuilder(command);
builder.redirectErrorStream(true);
Process p=builder.start();
I have solved my problem.
Here's the code sample first, explanation will follow;
String path = new
File(Main.class.getProtectionDomain().getCodeSource().getLocation()
.toURI()).getPath();
path=path.substring(0,path.length()-21);
// set up the command and parameter
String[] cmd = new String[3];
cmd[0] = "python";
cmd[1] = "analytics.py";
cmd[2] = "https://fortunamaritime.com.tr";
String command0="jar xf FortunaWebTracker.jar" +
"com/fortunamaritime/analytics.pycom/fortunamaritime/client_secrets.json" +
" com/fortunamaritime/webmasters.dat";
String command1 = cmd[0] + " " + cmd[1] + " " + cmd[2] + " " + startDate + " " +
endDate;
ProcessBuilder builder = new ProcessBuilder("cmd.exe", "/c", "cd " + path + " && " +
command0+"&&"+"cd "+path+"\\com\\fortunamaritime"+" && "+command1);
builder.redirectErrorStream(true);
Process p = builder.start();
My first mistake was to use absolute path, and the second problem arose when I got the path I wanted via:
File(Main.class.getProtectionDomain().getCodeSource().getLocation()
.toURI()).getPath();
//Jar's name is 21 characters long, minus that and I get the directory path
path=path.substring(0,path.length()-21);
The second problem was unpacking elements inside the jar so I used this command in cmd:
jar xf FortunaWebTracker.jar
com/fortunamaritime/analytics.py
com/fortunamaritime/client_secrets.json
com/fortunamaritime/webmasters.dat
then I could get it to work.
I am trying to run a command to read a string from a file inside a remote address (and I'm sure the file is there), this command works when I run it on the bash but it doesn't work when I run it in my java code.
Runtime rt = Runtime.getRuntime();
String[] command;
String line;
try {
command = new String[] {"sh", "-c", "\"sshpass " + "-p " + password + " ssh " + user + "#" + ip + " 'cat " + file.getAbsolutePath() + "'\"" };
Process mountProcess = rt.exec(command);
mountProcess.waitFor();
bufferedReader = new BufferedReader(new InputStreamReader(mountProcess.getInputStream()));
while ((line = bufferedReader.readLine()) != null) {
user_list.put(user, line);
}
bufferedReader.close();
bufferedReader = new BufferedReader(new InputStreamReader(mountProcess.getErrorStream()));
while ((line = bufferedReader.readLine()) != null) {
LOGGER.debug("Stderr: " + line);
}
bufferedReader.close();
} catch ...
No line is added to my user_list (so the line from getInputStream is null) and I get the following error from the logger in the code:
Stderr: sh: 1: sshpass: not found
If I use the exact same command on the bash it works and it prints the string I need.
sshpass -p password ssh remote#192.168.1.10 'cat /home/ID/ID'
Anyone knows why this is happening? thanks!
I'd suggest you don't need to use sh to wrap your command. Try
command = new String[] {"sshpass", "-p", password, "ssh", user + "#" + ip, "cat " + file.getAbsolutePath() };
If you need to use sh, then remove the escaped double quotes from the command string: you are sending those as literal characters:
command = new String[] {
"sh",
"-c",
String.format("sshpass -p %s ssh %s#%s 'cat %s'", password, user, ip, file.getAbsolutionPath())
};
If you're still getting "command not found", then you need to either specify the full path to sshpass, or ensure that its directory is in your PATH.
When doing this command with java the user is tomcat8 instead of root (when used in the bash terminal)
The solution that worked for me included some flags:
String.format("/usr/local/bin/sshpass -p %s /usr/bin/ssh -q -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no %s#%s 'cat %s'", password, user, ip, file.getAbsolutePath());
This is a part of my code to copy files from local to a remote machine
try {
Process cpyFileLocal = Runtime.getRuntime().exec("scp " + rFile+"*.csv" + " root#" + host + ":" + lFile);
InputStream stderr = cpyFileLocal.getErrorStream();
InputStreamReader isr = new InputStreamReader(stderr);
BufferedReader br = new BufferedReader(isr);
String line = null;
System.out.println("<ERROR>");
while ((line = br.readLine()) != null) {
System.out.println(line);
}
System.out.println("</ERROR>");
int exitVal = cpyFileLocal.waitFor();
System.out.println("Process exitValue: " + exitVal);
System.out.println("...." + cpyFileLocal.exitValue());
System.out.println("SCP COMMAND "+"scp "+rFile+"*.csv" +" root#"+host+":"+lFile);
System.out.println("Sending complete...");
} catch (Exception ex) {
ex.printStackTrace();
}
the output is...
<ERROR>
/opt/jrms/rmsweb/transfer/cn00/outgoing/*.csv: No such file or directory
</ERROR>
Process exitValue: 1
....1
SCP COMMAND scp /opt/jrms/rmsweb/transfer/cn00/outgoing/*.csv root#10.50.1.29:/opt/jrms/transfer/incoming/
but when I run the command in terminal on the local machine, it works fine
and when I run ll the files are there
-rwxr-xr-x 1 freddie freddie 140 Apr 22 09:13 gc00cn00150420092629.csv*
-rwxr-xr-x 1 freddie freddie 105 Apr 22 09:13 gc00cn00150420122656.csv*
Any help please
If you are using java 7 and above you should use ProcessBuilder instead of Runtime.getRuntime().exec() and in the ProcessBuilder you can specipied the execution directory:
ProcessBuilder pb = new ProcessBuilder("scp", rFile+"*.csv", "root#" + host + ":" + lFile);
Map<String, String> env = pb.environment();
env.put("VAR1", "myValue");
env.remove("OTHERVAR");
env.put("VAR2", env.get("VAR1") + "suffix");
pb.directory("directory where the csv files located");
Process p = pb.start();
When you run command with in bash with wildcards like * in it, bash will expand that command and in your case, replaces *.csv with list of files terminating with .csv, but in your java program, this won't happen.
According to this answer, you can do following:
Use file.listFiles() to get the list of files
Use file.getName().contains(string) to filter them if needed
Iterate over the array and perform scp or do it with whole list
or with thanks to #James Anderson comment add sh before scp in your command.
According to this, you should try:
Process cpyFileLocal = Runtime.getRuntime().exec(new String[] {"/bin/sh","-c", "scp " + rFile+"*.csv" + " root#" + host + ":" + lFile});
I tested with /bin/sh and /bin/bash, both copied the files over successfully
I am trying to run "tar -ztf /users/home/test.tar.gz | head -1" in Java, which worked when I tried to run it in unix command line directly.
The result of this command will list one line of the file/folder inside of the test.tar.gz.
for example: proj/test/test_dir
But I when I run it in java. it will give this error:
Running command: tar -ztf /users/home/test.tar.gz | head -1
[java] tar: Options `-[0-7][lmh]' not supported by *this* tar
[java] Try `tar --help' for more information.
Any idea what's wrong with it? why is it related to "specify drive and density" option?
The code I have run:
String s = null;
StringBuffer sbOutput = new StringBuffer();
StringBuffer errorInfo = new StringBuffer();
String[] cmd = {"tar", "-ztf", fileName, "|", "head", "-1"};
try
{
Runtime rt = Runtime.getRuntime();
System.out.println("Running command: " + cmd[0] + " " + cmd[1] + " " + cmd[2] + " " + cmd[3] + " " + cmd[4] + " " + cmd[5]);
Process p = rt.exec(cmd);
BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
//If there is an error - only show that
while ((s = stdError.readLine()) != null)
{
errorInfo.append(s + "\n");
}
if (errorInfo.length() > 0)
{
System.out.println(errorInfo.toString());
}
while ((s = stdInput.readLine()) != null) {
sbOutput.append(s + "\n");
}
// wait for end of command execution
try {
p.waitFor();
} catch (InterruptedException ie) {
new LogErrThread(ie).start();
ie.printStackTrace();
}
p.destroy();
if (sbOutput.length() > 0)
{
System.out.println(sbOutput.toString());
}
}
catch (IOException e)
{
new LogErrThread(e).start();
e.printStackTrace();
}
On the command line, the shell is doing the piping for you. Only the arguments before the | are passed to gtar. Your code incorrectly passes the pipe and the rest of the text as arguments to gtar.
Luckily, the solution is simple. You can simply read the first line yourself.
String[] cmd = {"gtar", "-ztf", fileName};
// ...
// Instead of current input loop.
s = stdInput.readLine();
if(s != null) {
sbOutput.append(s + "\n");
}
while (stdInput.readLine() != null) {
// Disregard. Reading to end to prevent hang.
}
To elaborate Matthew's point, the | operator is interpreted by the shell. To run your command without the shell, you would need to launch the programs separately and connect their pipes together (tricky in Java).
If you input is sanitized, you can invoke the shell and give it the command to run. Its the easier approach, though arguably less portable. In general, the SHELL environment variable contains the user's shell. Shells also have a defacto standardized -c option to pass them a command string in argv. If you invoke $SHELL -c [command string], you should get the behavior you want.