Link adb path from resource to execute shell commands in Java application - java

I'm looking for a way to run adb commands directly from a java application. While search on Stack Overflow I found the following solution for running shell commands,
public class Utils {
private static final String[] WIN_RUNTIME = {"cmd.exe", "/C"};
private static final String[] OS_LINUX_RUNTIME = {"/bin/bash", "-l", "-c"};
private Utils() {
}
private static <T> T[] concat(T[] first, T[] second) {
T[] result = Arrays.copyOf(first, first.length + second.length);
System.arraycopy(second, 0, result, first.length, second.length);
return result;
}
public static List<String> runProcess(boolean isWin, String... command) {
System.out.print("command to run: ");
for (String s : command) {
System.out.print(s);
}
System.out.print("\n");
String[] allCommand = null;
try {
if (isWin) {
allCommand = concat(WIN_RUNTIME, command);
} else {
allCommand = concat(OS_LINUX_RUNTIME, command);
}
ProcessBuilder pb = new ProcessBuilder(allCommand);
pb.redirectErrorStream(true);
Process p = pb.start();
p.waitFor();
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
String _temp = null;
List<String> line = new ArrayList<String>();
while ((_temp = in.readLine()) != null) {
//System.out.println("temp line: " + _temp);
line.add(_temp);
}
System.out.println("result after command: " + line);
return line;
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
This works perfectly, however I couldn't find a solution to add the adb.exe path into the shell commands so that I can execute adb commands.
My project structure is given below,
I'm trying to append the adb path along with the system default shell path using the following way,
Utils.runProcess(true, "/resources/adb.exe devices");
Is there any way to append the adb.exe path from resources into the shell command?

Use the full path to adb.exe that way you don't need to add it to %PATH%.
eg. If you open cmd and run C:\...\adb.exe devices it will work
Alternatively execute this command in the shell to set your path,
setx path "%path%;C:\..."
Edit: Add the adb.exe in your resources folder in the same package as your calling class. Then load it and write it to another location that you happen to know (or generate a path relative to where your jar is located eg.System.getProperty("user.dir");
)
ClassLoader classLoader = getClass().getClassLoader();
File file = new File(classLoader.getResource("adb.exe").getFile());
// now copy this file to a location you already know eg. C:\...\temp\adb.exe
And then invoke adb.exe with the path that you have

Related

Why am I getting IOException : "Cannot run program ./shellScript.sh error = 2, No such file or directory in Java?

The following Main.java code simply tries to simulate the following linux command:
cd /dir1/dir2
./shellScript.sh
The program below works only if the executable Main.jar sits within /dir1/dir2, not outside of /dir1/dir2. How do I modify the program below so that Main.jar can sit anywhere on the file system?
public class Main {
public static String runCmdLineProcess(String commandStr){
String returnVal = "";
Runtime r = Runtime.getRuntime();
try {
Process p = r.exec(commandStr);
BufferedReader b = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = "";
while ((line = b.readLine()) != null){
returnVal += line + "\n";
}
}
catch(IOException ex){
ex.printStackTrace();
}
return returnVal;
}
public static void runProcessBuilder(String scriptPath){
String[] cmd = {scriptPath};
try {
runCmdLineProcess("cd /dir1/dir2");
Runtime.getRuntime().exec(cmd);
}
catch (IOException ex){
ex.printStackTrace();
}
}
public static void main(String[] args){
runProcessBuilder("./shellScript.sh"); // <-- works if I run from inside "/dir1/dir2".
//But if I'm outside "dir2", get an error message
// saying "Cannot run program "./shellScript.sh": error = 2, No such file or directory
}
}
You should use ProcessBuilder to launch or one of the overloads of exec. You need to specify the pathname to the script and pass the same pathname as the current directory to run the script in:
File pwd = new File("/dir1/dir2");
String shell = new File(pwd, "shellScript.sh").toString();
ProcessBuilder pb = new ProcessBuilder(shell);
// No STDERR => merge to STDOUT - or call redirectError(File)
pb.redirectErrorStream(true);
// Set CWD for the script
pb.directory(pwd);
Process p = pb.start();
// Move STDOUT to the output stream (or original code to save as String)
try(var stdo = p.getInputStream()) {
stdo.transferTo(stdout);
}
int rc = p.waitFor();

Using ADB pull command in a Java program

I can't get the ADB command to work in Java. The adb command works when inputting it directly to the command line. But when running it in Java, I get error
Cannot run program "adb -s shell": CreateProcess error=2,file not found
What is the proper syntax for running Windows Command line from Java?
The adb command "adb devices" works in the Java application. The command I'm trying to run is "adb pull sdcard/Download/symmetri.txt C:/Users/myUsername/Downloads/Sources", which works in the command prompt, but not from within the Java application. My code is:
public void FilePush() {
try{
String androidFilePath = "sdcard/Download/symmetri.txt ";
String windowsFilePath = "C:\\Users\\myUsername\\Downloads\\Sources\"";
List<String> cmd = new LinkedList<>();
cmd.add("adb -s shell");
cmd.add("adb");
cmd.add("pull");
cmd.add(androidFilePath);
cmd.add(windowsFilePath);
ProcessBuilder builder = new ProcessBuilder(cmd);
builder.redirectErrorStream(true);
Process p = builder.start();
}
catch (IOException e) {
e.printStackTrace();
}
}
I also tried without adb -s shell" and with
String androidFilePath = "\"/storage/sdcard0/Download/symmetri.txt\"";
String windowsFilePath = "\"C:\\Users\\myUsername\\Downloads\\Sources\\"";
But got the same error
Remove the line
cmd.add("adb -s shell");
as you don't want shell but pull.
Thx to the helpful people here, I finally got it to work! I first needed to run adb.exe as a command, before I could run any adb commands. I needed to remove ("adb -s shell") from my list of commands. I was trying to acces a file in the external storage instead of the internal one which needs root access
(Although I have no clue why I can acces the exact same file in a Command prompt window without permitting root acces). If you want to access external files, add root access with
adb root
Full code that works for anyone who might face the same issue :
public void FilePush() {
try{
String androidFilePath = "/storage/emulated/0/Android/data/com.example.myapp/files/myfile.txt";
String windowsFilePath = "C:\\Users\\myUsername\\Downloads\\Sources";
List<String> cmd = new LinkedList<>();
cmd.add("C:\\Users\\myUsername\\AppData\\Local\\Android\\Sdk\\platform-tools\\adb.exe");
cmd.add("pull");
cmd.add(androidFilePath);
cmd.add(windowsFilePath);
ProcessBuilder pb = new ProcessBuilder(cmd);
pb.redirectErrorStream(true);
String line = "null";
pb.redirectErrorStream(true); // can use these 2 line if you want to see output or errors in file.
pb.redirectOutput(new File("C:\\Users\\myUsername\\Downloads\\Sources\\logs.txt"));
Process p = pb.start();
while(p == null)
Thread.sleep(1000);
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream()));
Pattern pattern = Pattern.compile("^([a-zA-Z0-9\\-]+)(\\s+)(device)");
Matcher matcher;
while ((line = in.readLine()) != null) {
if (line.matches(pattern.pattern())) {
matcher = pattern.matcher(line);
if (matcher.find()) ;
}
}
}
catch (Exception e) {
e.printStackTrace();
}
}

Java Runtime.exec fail with space in linux

I searched a lot but did not find the solution.
My goal is using java to call commands and get output in windows and linux. I found Runtime.exec method and did some experiments.
Everything went ok except when there's space in the command parameters.
Test code as below, also in github.
The code works well on windows, but in linux, output is empty:
import java.io.BufferedReader;
import java.io.InputStreamReader;
public class Main {
public static void main(String[] args) {
try {
Runtime rt = Runtime.getRuntime();
String[] commandArray;
if (isWindows()) {
commandArray = new String[]{"cmd", "/c", "dir", "\"C:\\Program Files\""};
} else {
commandArray = new String[]{"ls", "\"/root/a directory with space\""};
}
String cmd = String.join(" ",commandArray);
System.out.println(cmd);
Process process = rt.exec(commandArray);
BufferedReader input = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String result = "";
String line = null;
while ((line = input.readLine()) != null) {
result += line;
}
process.waitFor();
System.out.println(result);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
public static boolean isWindows() {
String OS = System.getProperty("os.name").toLowerCase();
return (OS.indexOf("win") >= 0);
}
}
if I execute the printed command in bash directly, then the output is as expected.
[root#localhost javatest]# javac Main.java
[root#localhost javatest]# java Main
ls "/root/a directory with space"
[root#localhost javatest]# ls "/root/a directory with space"
a.txt b.txt
[root#localhost javatest]#
Can anyone explain why and give ways to solve?
There are two versions of exec.
exec(String command)
Here you specify a command in a similar way to how you would do it on the command-line, i.e. you need to quote arguments with spaces.
cmd /c dir "C:\Program Files"
exec(String[] cmdarray)
Here you specify the arguments separately, so the arguments are given as-is, i.e. without quotes. The exec method will take care of any spaces and quote-characters in the argument, correctly quoting and escaping the argument as needed to execute the command.
cmd
/c
dir
C:\Program Files
So, remove the extra quotes you added:
if (isWindows()) {
commandArray = new String[] { "cmd", "/c", "dir", "C:\\Program Files"};
} else {
commandArray = new String[] { "ls", "/root/a directory with space"};
}

Run Shell Commands using Java provides problems

public class RunBashCommand {
public synchronized boolean RunInBash(String command) {
System.out.println("CMD: "+command);
/*String s; not working this code also
Process p;
try {
Process p = Runtime.getRuntime().exec(command);
BufferedReader br = new BufferedReader(
new InputStreamReader(p.getInputStream()));
while ((s = br.readLine()) != null)
System.out.println("line: " + s);
p.waitFor();
System.out.println ("exit: " + p.exitValue());
PrintBufferReader(getError(p));
p.destroy();
} catch (Exception e) {
e.printStackTrace();
}*/
try {
Process p = new ProcessBuilder("/bin/sh", command).start();
/*Process p = new ProcessBuilder("/bin/bash", command).start();*/
PrintBufferReader(getError(p));
/*p.destroy();*/
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
private static BufferedReader getOutput(Process p) {
return new BufferedReader(new InputStreamReader(p.getInputStream()));
}
private static BufferedReader getError(Process p) {
return new BufferedReader(new InputStreamReader(p.getErrorStream()));
}
private void PrintBufferReader(BufferedReader br) throws IOException {
int value = 0;
String s = "";
while((value = br.read()) != -1)
{
char c = (char)value;
s = s+c;
}
System.out.println("EEEE: "+s);
}
}
I tried this code, but it did not work.
following output came:
CMD: cd /home/jeevan/workspace/apb_proj/; source init.csh
EEEE: /bin/sh: cd /home/jeevan/workspace/apb_proj/; source init.csh: No such file or directory
CMD: cd /home/jeevan/workspace/apb_proj/verif/compile/; make clean; make compile; make elab
EEEE: /bin/sh: cd /home/jeevan/workspace/apb_proj/verif/compile/; make clean; make compile; make elab: No such file or directory
CMD: sh /home/jeevan/workspace/apb_proj/verif/test_lib/src/apb_test31/runme.csh
EEEE: /bin/sh: sh /home/jeevan/workspace/apb_proj/verif/test_lib/src/apb_test31/runme.csh: No such file or directory
can some one help?
You're effectively running:
/bin/sh "cd /home/jeevan/workspace/apb_proj/; source init.csh"
When you run /bin/sh this way, it treats its first argument as the name of a file to execute as a shell script. Of course, there's no file named "cd /home/jeevan/workspace/apb_proj/; source init.csh", so you get an error message.
The correct way to invoke sh with a command as an argument is like this:
/bin/sh -c "cd /home/jeevan/workspace/apb_proj/; source init.csh"
Using process builder, you'd do:
Process p = new ProcessBuilder("/bin/sh", "-c", command).start();
The next problem that you're likely to run into is that it appears that the command you're trying to invoke is a csh command, not an sh command. "source" is a csh command, and the file you're trying to source is called "init.csh". So maybe you want to invoke csh instead of sh:
Process p = new ProcessBuilder("/bin/csh", "-c", command).start();
You need to split command arguments into separate parameters: not ProcessBuilder("bin/sh", "cd foo/bar") but ProcessBuilder("bin/sh", "cd", "foo/bar").
You can't use shell metacharacters (like ";") too. To run multiple commands, you have to start multiple processes.
Put all your commands into a List and pass it as the argument to the ProcessBuilder. As an alternative you can start the shell process, get it's OutputStream and write commands into this stream to execute them.

Runtime.getRuntime.exec() not working for the linux command "tar -xvf filename.tar"

I am trying to untar a file on a Unix machine, using a Java batch application.
Source Code:
String fileName = "x98_dms_12";
Runtime.getRuntime().exec("gunzip "+ fileName + ".tar.gz");
System.out.println(" Gunzip:"+"gunzip "+ fileName + ".tar.gz");
Runtime.getRuntime().exec("tar -xvf "+ fileName + ".tar");
System.out.println(" Extract:tar -xvf "+ fileName + ".tar");
Problem Description:
When I run the batch program it does not (completely) work. Only the gunzip command works, converting my fileName.tar.gz to fileName.tar. But the untar command does not seem to do anything, and there is no error or exception in my log or Unix console.
When I run the same commands in a Unix prompt they work fine.
Notes:
The path of execution is correct because it converts my *.tar.gz to *.tar
I cannot use "tar -zxvf fileName.tar.gz" since the attribute "z" does not work on my system.
There is no error or exception thrown.
Please do help.
A couple of things:
The tar command will expand a file relative to your working directory, which might need to be set for your Java Process objects
You should wait for the unzip process to complete before launching into the untar process
You should process the output streams from the processes.
Here is a working example that you can extend/adapt. It uses a separate class to deal with the process output streams:
class StreamGobbler implements Runnable {
private final Process process;
public StreamGobbler(final Process process) {
super();
this.process = process;
}
#Override
public void run() {
try {
final BufferedReader reader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line = null;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
reader.close();
} catch (final Exception e) {
e.printStackTrace();
}
}
}
public void extractTarball(final File workingDir, final String archiveName)
throws Exception {
final String gzFileName = archiveName + ".tar.gz";
final String tarFileName = archiveName + ".tar";
final ProcessBuilder builder = new ProcessBuilder();
builder.redirectErrorStream(true);
builder.directory(workingDir);
builder.command("gunzip", gzFileName);
final Process unzipProcess = builder.start();
new Thread(new StreamGobbler(unzipProcess)).start();
if (unzipProcess.waitFor() == 0) {
System.out.println("Unzip complete, now untarring");
builder.command("tar", "xvf", tarFileName);
final Process untarProcess = builder.start();
new Thread(new StreamGobbler(untarProcess)).start();
System.out.println("Finished untar process. Exit status "
+ untarProcess.waitFor());
}
}
The code below will print the output of the command executed. Check if it returns any error.
Process p = Runtime.getRuntime().exec("tar -xvf "+ fileName + ".tar");
BufferedReader br = new BufferedReader(new InputStreamReader(p.getInputStream()));
String line = null;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
The problem is the commands which we give is UNIX command so it wont work in windows environment. I had written a script file to overcome this problem thanks all for you help. The Runtime.getRuntime.exec() will take some time to execute the command given so after each exec() give thread.wait(3000) to complete the process and goto next thread.

Categories