Using java, I run cmd to be able to execute a ttl file.
My code:
try {
String[] command = new String[] {"cmd.exe", "/c", "cd C:\software\teraterm", "& TTPMacro C:\file\execute.ttl param1 param2 param3"};
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
} catch (Exception e) {
throw e;
}
But I want cmd to be run as admin
so I added runas /savecred /user:.\Administrator in my code,
but it is not working anymore:
try {
String[] command = new String[] {"cmd.exe", "/c", "runas /savecred /user:.\\Administrator", "cd C:\software\teraterm", "& TTPMacro C:\file\execute.ttl param1 param2 param3"};
Process p = Runtime.getRuntime().exec(command);
p.waitFor();
} catch (Exception e) {
throw e;
}
What is wrong with this?
I've updated code to check input stream, but there is no output
try {
String[] command = new String[] {"cmd.exe", "/c", "runas /savecred /user:.\\Administrator", "cd C:\software\teraterm", "& TTPMacro C:\file\execute.ttl param1 param2 param3"};
Process p = Runtime.getRuntime().exec(command);
BufferedReader br=new BufferedReader(new InputStreamReader(p.getInputStream()));
String l;
while((l=br.readLine()) != null){
System.out.print(l);
}
p.waitFor();
} catch (Exception e) {
throw e;
}
Meta: although not stated, this Q and A is only for Windows and is useless on other systems
runas can only run a program (with arguments); cd is a builtin function of cmd not a program. Also cmdone & cmdtwo is a feature of cmd, not something runas can do. You need to put your commands into a cmd /c invocation, much as you did for running directly from Java Runtime.exec (which also can only run a single program), except that runas requires its program-to-run to be exactly one argument, not optionally several that are (re)joined. So:
String[] command = new String[]{"runas","/user:administrator","/savecred", // these must be separated args
"cmd /c cd directory & executable data"}; // these must be all one argument
Also in Java String literals, backslash is used as an escape character, so you must double each one (in pathnames or otherwise).
Finally if runas doesn't have already-saved credentials it prompts for the password. This prompt is not a line, so you can't read it with BufferedReader.readLine(). You can read it as bytes (into an array) with the input stream, or chars (ditto) with the InputStreamReader. However, as something of a hack, since runas doesn't pass its own stdhandles to its child but instead a new console window, you can just ignore the prompt and always write the password to p.getOutputStream() whether it's needed or not, and if it's not it will be discarded.
Related
I have seen similiar questions on this site, but none of them seem to address/solve my problem, so I figured there is something specifically wrong with my program. I am trying to execute a very simple command, which is to take a string of a process name from a textfield input and concatenate it to a command to return and print the title of the window. This is my code:
String line;
Process p = null;
try
{
String command = "tasklist /v /fo list /fi \"imagename eq " + tf.getText().trim() + "*\"| find /i \"window title:\"\n";
p = Runtime.getRuntime().exec(command);
BufferedReader input =
new BufferedReader(new InputStreamReader(p.getInputStream()));
System.out.println(command);
while ((line = input.readLine()) != null)
{
line = line.trim();
System.out.println(line);
}
System.out.println("done");
}
catch (IOException ioException)
{
ioException.printStackTrace();
}
However, the line returned by the InputStream is always null, even though if I put the command used in .exec() into cmd (I printed it so I know they are the exact same), it works properly, albeit after a 5 seconds or so of delay. I tried it with 2 different process names and they both worked on cmd, but not in this java program. This is the output of the above code, in case that helps (the blank line is presumably from the \n at the end of the command string):
tasklist /v /fo list /fi "imagename eq notepad*"| find /i "window title:"
done
I tried adding p.waitFor() after calling .exec(), but that didn't seem to change anything. So what am I doing wrong here?
You have two problems with launching the command. Firstly you are ignoring error stream so don't see the actual problem.
Replace p = Runtime.getRuntime().exec(command); with ProcessBuilder to get access to error message:
ProcessBuilder pb = new ProcessBuilder(command);
pb.redirectErrorStream();
p = pb.start();
This will tell you that tasklist is not a process. Normally using full pathname would fix this type of error, but as you are using pipe the whole command must sent to to CMD.EXE to interpret pipe components correctly. Run CMD.EXE then your piped command:
ProcessBuilder pb = new ProcessBuilder("cmd.exe", "/c", command);
pb.redirectErrorStream();
p = pb.start();
Prints:
tasklist /v /fo list /fi "imagename eq notepad*"| find /i "window title:"
Window Title: Notepad++
done
It's also easier to read STDOUT with simple transfer:
try(var stdout = p.getInputStream()) {
stdout.transferTo(System.out); // or where-ever
}
I can not find anywhere that specificity explain what can be store inside of String[] cmdarray of Process exec(String[] cmdarray) method. I found some places cmdarray to store an array command or location of file and remote server name. So I wonder what exactly we can store inside of String[] cmdarray?
The first element of the array is the command, such as cmd. The others are arguments. For example:
try {
Process p = Runtime.getRuntime().exec(new String[] {"cmd", "/c", "echo", "This", "is", "an", "argument"});
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String s;
while((s = reader.readLine()) != null) {
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
Here "/c", "echo", "This", "is", "an", and "argument" are all arguments for the command cmd. The output is:
This is an argument
If you want to run multiple commands, you must use a double ampersand to indicate that another command is starting:
try {
Process p = Runtime.getRuntime().exec(new String[] { "cmd", "/c", "echo", "This", "is", "an", "argument",
"&&", "echo", "this", "command", "snuck", "in" });
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String s;
while ((s = reader.readLine()) != null) {
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
Here each command is being sent to cmd. I'm not positive, but I believe you must start a new process to send commands elsewhere. The output is:
This is an argument
this command snuck in
Read this for more info: https://stackoverflow.com/a/18867097/5645656
According to docs:
Executes the specified command and arguments in a separate process.
Consider it as your command line interface inside JVM. It takes all process names that can be invoked using CMD. For eg: you can pass String chromium to exec in Ubuntu and you will notice that chromium is launched.
Consider the following code:
String commandf = "ls /etc | grep release";
try {
// Execute the command and wait for it to complete
Process child = Runtime.getRuntime().exec(commandf);
child.waitFor();
// Print the first 16 bytes of its output
InputStream i = child.getInputStream();
byte[] b = new byte[16];
i.read(b, 0, b.length);
System.out.println(new String(b));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
The program's output is:
/etc:
adduser.co
When I run from the shell, of course, it works as expected:
poundifdef#parker:~/rabbit_test$ ls /etc | grep release
lsb-release
The internets tell me that, due to the fact that pipe behavior isn't cross-platform, the brilliant minds who work in the Java factory producing Java can't guarantee that pipes work.
How can I do this?
I am not going to do all of my parsing using Java constructs rather than grep and sed, because if I want to change the language, I'll be forced to re-write my parsing code in that language, which is totally a no-go.
How can I make Java do piping and redirection when calling shell commands?
Write a script, and execute the script instead of separate commands.
Pipe is a part of the shell, so you can also do something like this:
String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};
Process p = Runtime.getRuntime().exec(cmd);
I ran into a similar problem in Linux, except it was "ps -ef | grep someprocess".
At least with "ls" you have a language-independent (albeit slower) Java replacement. Eg.:
File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
if (file.matches(.*some.*)) { System.out.println(file); }
}
With "ps", it's a bit harder, because Java doesn't seem to have an API for it.
I've heard that Sigar might be able to help us:
https://support.hyperic.com/display/SIGAR/Home
The simplest solution, however, (as pointed out by Kaj) is to execute the piped command as a string array. Here is the full code:
try {
String line;
String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}
As to why the String array works with pipe, while a single string does not... it's one of the mysteries of the universe (especially if you haven't read the source code). I suspect that it's because when exec is given a single string, it parses it first (in a way that we don't like). In contrast, when exec is given a string array, it simply passes it on to the operating system without parsing it.
Actually, if we take time out of busy day and look at the source code
(at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29), we find that is exactly what is happening:
public Process [More ...] exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
Create a Runtime to run each of the process. Get the OutputStream from the first Runtime and copy it into the InputStream from the second one.
#Kaj accepted answer is for linux. This is the equivalent one for Windows:
String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);
Consider the following code:
String commandf = "ls /etc | grep release";
try {
// Execute the command and wait for it to complete
Process child = Runtime.getRuntime().exec(commandf);
child.waitFor();
// Print the first 16 bytes of its output
InputStream i = child.getInputStream();
byte[] b = new byte[16];
i.read(b, 0, b.length);
System.out.println(new String(b));
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
The program's output is:
/etc:
adduser.co
When I run from the shell, of course, it works as expected:
poundifdef#parker:~/rabbit_test$ ls /etc | grep release
lsb-release
The internets tell me that, due to the fact that pipe behavior isn't cross-platform, the brilliant minds who work in the Java factory producing Java can't guarantee that pipes work.
How can I do this?
I am not going to do all of my parsing using Java constructs rather than grep and sed, because if I want to change the language, I'll be forced to re-write my parsing code in that language, which is totally a no-go.
How can I make Java do piping and redirection when calling shell commands?
Write a script, and execute the script instead of separate commands.
Pipe is a part of the shell, so you can also do something like this:
String[] cmd = {
"/bin/sh",
"-c",
"ls /etc | grep release"
};
Process p = Runtime.getRuntime().exec(cmd);
I ran into a similar problem in Linux, except it was "ps -ef | grep someprocess".
At least with "ls" you have a language-independent (albeit slower) Java replacement. Eg.:
File f = new File("C:\\");
String[] files = f.listFiles(new File("/home/tihamer"));
for (String file : files) {
if (file.matches(.*some.*)) { System.out.println(file); }
}
With "ps", it's a bit harder, because Java doesn't seem to have an API for it.
I've heard that Sigar might be able to help us:
https://support.hyperic.com/display/SIGAR/Home
The simplest solution, however, (as pointed out by Kaj) is to execute the piped command as a string array. Here is the full code:
try {
String line;
String[] cmd = { "/bin/sh", "-c", "ps -ef | grep export" };
Process p = Runtime.getRuntime().exec(cmd);
BufferedReader in =
new BufferedReader(new InputStreamReader(p.getInputStream()));
while ((line = in.readLine()) != null) {
System.out.println(line);
}
in.close();
} catch (Exception ex) {
ex.printStackTrace();
}
As to why the String array works with pipe, while a single string does not... it's one of the mysteries of the universe (especially if you haven't read the source code). I suspect that it's because when exec is given a single string, it parses it first (in a way that we don't like). In contrast, when exec is given a string array, it simply passes it on to the operating system without parsing it.
Actually, if we take time out of busy day and look at the source code
(at http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/lang/Runtime.java#Runtime.exec%28java.lang.String%2Cjava.lang.String[]%2Cjava.io.File%29), we find that is exactly what is happening:
public Process [More ...] exec(String command, String[] envp, File dir)
throws IOException {
if (command.length() == 0)
throw new IllegalArgumentException("Empty command");
StringTokenizer st = new StringTokenizer(command);
String[] cmdarray = new String[st.countTokens()];
for (int i = 0; st.hasMoreTokens(); i++)
cmdarray[i] = st.nextToken();
return exec(cmdarray, envp, dir);
}
Create a Runtime to run each of the process. Get the OutputStream from the first Runtime and copy it into the InputStream from the second one.
#Kaj accepted answer is for linux. This is the equivalent one for Windows:
String[] cmd = {
"cmd",
"/C",
"dir /B | findstr /R /C:"release""
};
Process p = Runtime.getRuntime().exec(cmd);
I have a Java-App, which should execute an sh command.
My command looks like sudo /bin/sh -c "echo 7 > /sys/class/gpio/export" and when I execute this in the command prompt of my computer it works, but not with my Java-Programm.
The Programm-line looks like this:
System.out.println(CmdExecutor.execute("sudo /bin/sh -c \"echo 7 > /sys/class/gpio/export\""));
public class CmdExecutor {
public static String execute(String[] cmd) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
} catch (IOException | InterruptedException e) {
}
return output.toString();
}
public static String execute(String cmd) {
StringBuffer output = new StringBuffer();
Process p;
try {
p = Runtime.getRuntime().exec(cmd);
p.waitFor();
BufferedReader reader = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
output.append(line).append("\n");
}
} catch (IOException | InterruptedException e) {
}
return output.toString();
}
}
Can someone help me?
I see two issues:
Multiple arguments need to be split in Java already.
Authentication with sudo.
Multiple arguments need to be split.
If you run exec("a b"), the system will look for a command named a b as one single String command name.
If you run exec("a", "b"), the system will look for a command namedaand passb` as argument to that program.
So what you want to do is execute("sudo", "/bin/sh", "-c", "echo 7 > /sys/class/gpio/export").
sudo might require authentication
When you execute commands with sudo, an authentication will be performed. If you execute multiple sudo commands from the same process, the system will cache the authentication for convenience, but basically the authentication is required.
The authentication with sudo usually means that you need to supply a password.
The reason why you sudo this is that /sys/class/gpio/export has permissions -w------- (200) owned by root root, which means nobody can read it and only root can write it.
You have a few options:
Change the permissions of that file so that everybody can write it (not recommended): chmod a+w /sys/class/gpio/export.
Change the permissions of that file so that the user in question can write it: setfacl -m user:cher:w /sys/class/gpio/export - note that this only works if your sysfs is mounted with acl option, and usually it isn't. I don't know if it's even possible to mount sysfs with acl option, I haven't tried myself.
Pipe the password to the sudo command: exec("echo password | sudo /bin/sh -c \"echo 7 > /sys/class/gpio/export\"") WARNING THIS IS DANGEROUS!!!
Use a graphical sudo replacement like kdesudo
Change your sudoers configuration so that the user in question never needs to enter password for sudo - not recommended.