Any way to run shell commands on android programmatically?
Asked Answered
M

5

44

Is there any way to run terminal commands on my application and then access the data on my UI? Specifically top.

Motive answered 28/7, 2010 at 6:2 Comment(0)
A
41

Check out Log Collector as an example. Here is the relevant file.

The key is here:

ArrayList<String> commandLine = new ArrayList<String>();
commandLine.add("logcat");//$NON-NLS-1$
[...]

Process process = Runtime.getRuntime().exec(commandLine);
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
Aga answered 28/7, 2010 at 6:12 Comment(4)
so basically if I would like to run top, I could just replace commandLine with top and it should work fine right? I am getting an error so I guess I will need a little more help.. thanks..Motive
Do I need to surround the Process process = Runtime.getRuntime().exec(commandLine); with a try and catch because I keep getting this throws IOException. I have looked at the java examples seem to handle it that way...Motive
Yes.. again, check out the link to see everything they do. You definitely need to do error checking - don't expect 'top' to be there on all phones.Aga
This is now banned by GoogleKaspar
M
21

Okay this is what exactly worked for me just in case anyone needs it in the future... :)

Surround in try and catch

try {
    Process process = Runtime.getRuntime().exec("top -n 1 -d 1");
    BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
} catch (InterruptedException e) {
    e.printStackTrace();
}
Motive answered 20/8, 2010 at 4:39 Comment(3)
When I do that followed by String result = null; result = bufferedReader.readLine(); Log.i("OnClickListener", "result: " + result); It displays only "result:" (the 'result' string is empty). Any thoughts as to why this would be? Also, when I try process = Runtime.getRuntime().exec("/system/bin/ping abc"); instead, the 'result' string is null. Both of those commands work as expected from an 'adb shell' terminal.Aroma
Sorry @DavidDoria, I have not touched android in 2 years now. It was something I tried my hand at while in college. You would be better off posting it as a question.Motive
I got it working. The problem was the that the first line from 'top' WAS empty (a new line, to create the table). The case of it returning null when there was actually output was because I needed to get stderror (getErrorStream) instead of stdout (getInputStream).Aroma
G
11

We, can execute commands as follow, i was succesfull in doing this....! try like this, here we need to specify the complete path of command. to get the complete path of commmand, at ur terminal (android) type

*$ which ls

/system/bin*

try {

    // Executes the command.

    Process process = Runtime.getRuntime().exec("/system/bin/ls /sdcard");

    // Reads stdout.
    // NOTE: You can write to stdin of the command using
    //       process.getOutputStream().
    BufferedReader reader = new BufferedReader(
            new InputStreamReader(process.getInputStream()));
    int read;
    char[] buffer = new char[4096];
    StringBuffer output = new StringBuffer();
    while ((read = reader.read(buffer)) > 0) {
        output.append(buffer, 0, read);
    }
    reader.close();

    // Waits for the command to finish.
    process.waitFor();

    return output.toString();
} catch (IOException e) {

    throw new RuntimeException(e);

} catch (InterruptedException e) {

    throw new RuntimeException(e);
}
Grandsire answered 16/4, 2011 at 14:2 Comment(0)
E
5

it also depends on what you are running in the terminal... if you are running "cat" on a file you can also do it like this.

final private String MEM_FILE = "/proc/meminfo";

public Long readMem() {
    String[] segs;
    FileReader fstream;
    try {
        fstream = new FileReader(MEM_FILE);
    } catch (FileNotFoundException e) {
        Log.e("readMem", "Could not read " + MEM_FILE);
        return false;
    }
    BufferedReader in = new BufferedReader(fstream, 500);
    String line;
    try {
        while ((line = in.readLine()) != null) {
            if (line.indexOf("MemTotal:") > 0) {
                Log.e("MemTotal", line);
                segs = line.trim().split("[ ]+");
                memTotal = Long.parseLong(segs[1]);
            }
            if (line.indexOf("MemFree:") > 0) {
                Log.e("MemFree", line);
                segs = line.trim().split("[ ]+");
                memFree = Long.parseLong(segs[1]);
            }
        }
        updateMem(); //call function to update textviews or whatever
        return true;
    } catch (IOException e) {
        Log.e("readMem", e.toString());
    }
    return false;
}

EDIT: There is a perfect example for you in the android labs project called netmeter. There is a class called Top.java that actually does exactly what you want and it is used in TaskList.java to be displayed. http://code.google.com/p/android-labs/source/browse/#svn/trunk/NetMeter/src/com/google/android/netmeter

Excise answered 28/7, 2010 at 11:24 Comment(4)
Not cat, I specifically want to run top and get the live results displayed on the screen. I am aware of applications which emulate the terminal on which I have found top working, so I was wondering how I could just pull the results of top and display them on the UI.Motive
See my edits above for a working example of exactly what you want.Excise
Hey thanks... Need to spend some time here, but I think this is it... You have been a great help!Motive
line.indexOf should be > -1 instead of > 0. -1 mean not foundYorke
D
2

For Kotlin enthusiasts, you can use the following

fun executeShell() {
    val command: String = "top -n 1"
    try {
        val process: Process = Runtime.getRuntime().exec(command)
        // Read the lines using BufferedReader
        BufferedReader(InputStreamReader(process.inputStream)).forEachLine {
            // Do something on each line read
            Log.d(this::class.java.canonicalName, "$it")
        }
    } catch (e: InterruptedException) {
        Log.w(this::class.java.canonicalName, "Cannot execute command [$command].", e)
    } catch (e: Exception) {
        Log.e(this::class.java.canonicalName, "Cannot execute command [$command].", e)
    }
}

You won't even have to take care of closing the buffer, as forEachLine extension function takes care of it.

Despoil answered 9/2, 2021 at 15:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.