6

对于我的一生,我无法让我的应用程序从 su shell 中调用busybox的进程中获取响应。

我尝试了三种不同的方法,也尝试了这三种方法的组合以使其工作,但我永远无法使用busybox从任何东西中获得输出,只有其余的命令。

更具体地说,我可以让它返回和之类的命令ls /datacat suchandsuch.file但是任何以“busybox”开头的东西(即busybox mount,busybox free)都不会显示任何内容。

这是对我来说最接近的方法,此代码适用于ls /data,但不是“busybox free”

这将运行命令(大部分),并返回一个空字符串,而不是从输入流中无休止地循环。

Process p;
try {
    p = Runtime.getRuntime().exec(new String[]{"su", "-c", "/system/bin/sh"});
    DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
    stdin.writeBytes("ls /data\n");
    DataInputStream stdout = new DataInputStream(p.getInputStream());
    byte[] buffer = new byte[4096];
    int read = 0;
    String out = new String();

    while(true){
        read = stdout.read(buffer);
        out += new String(buffer, 0, read);
        if(read<4096){
            break;
    }
}
    Toast.makeText(getApplicationContext(), out, Toast.LENGTH_SHORT).show();
} catch (IOException e) {
    e.printStackTrace();
}

底部附近的 toast 显示从 开始的所有内容ls /data,但是当更改为 busybox 的任何内容时,它的空白或 null。

我也尝试过这两种方法,但都没有奏效。(我在命令运行后将进程传递给他们。)

当您点击方法的按钮时,这两种方法总是会导致应用程序冻结。

String termReader(Process process){
    BufferedReader reader = new BufferedReader(
        new InputStreamReader(process.getInputStream()));   
    try {
        int i;
        char[] buffer = new char[4096];
        StringBuffer output = new StringBuffer();

        while ((i = reader.read(buffer)) > 0) 
            output.append(buffer, 0, i);

        reader.close();
        return output.toString();
    } catch (IOException e) {
        e.printStackTrace();
        return e.getMessage();
    }
}


String processReader(Process process){
    InputStream stdout = process.getInputStream();
    byte[] buffer = new byte[1024];
    int read;
    String out = new String();
    while(true){
        try {
            read = stdout.read(buffer);
            out += new String(buffer, 0, read);
            if(read<1024){
            break;
        }
        } catch (IOException e) {
            e.printStackTrace();                            
        }
    }
    return out;
}

没有堆栈跟踪可以使用,所以我开始有点难过。

使用下面提出的代码进行编辑,嗯,下面 :D 我对其进行了一些更改,使其成为一键运行的东西,以便于故障排除和测试。

当它尝试读取输入流时,它也会冻结,如果我stdin.writeBytes("exit\n")在尝试读取流之前调用它会给我关闭终端的空白答案,如果我在之后调用它,它会无限循环。

void Run() {
    String command = "busybox traceroute\n";

    StringBuffer theRun = null;
    try {
        Process process = Runtime.getRuntime().exec("su");
        DataOutputStream stdin = new DataOutputStream(process.getOutputStream());
        stdin.writeBytes(command);
        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) {
            theRun = output.append(buffer, 0, read);
    }

    reader.close();
    process.waitFor();

    } catch (IOException e) {
        throw new RuntimeException(e);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    Toast.makeText(getApplicationContext(), theRun, Toast.LENGTH_SHORT).show();
}

似乎它跳过了第一行(每次调用命令时都会得到的busybox信息行)并且没有捕获其余数据。我已经尝试了所有我能想到的变体来让它正常工作:/

如果有人对此有所了解,我将不胜感激:)

4

2 回答 2

3

这是一个快速的解决方案......这是我为此创建的实用程序类。您可以使用本机 shell,如果设备已获得 root 权限,则可以使用 root shell,或者设置自定义 shell。干得好。

https://github.com/jjNford/android-shell

于 2012-04-10T19:15:53.953 回答
1

我找到了一种解决方法。

首先,在我的情况下,运行与busybox相关的命令永远不会通过他们的InputStream返回它们的输出,无论我尝试什么方法(我试过很多哈哈)。

这就是我发现我可以做到的。这有点乏味,并且不会为您提供完整的输出,但是如果您想要依赖某个命令是否正确触发(在我的情况下,如果我无法比较所有内容,我的应用程序将无法正常工作跑了。)

您无法从流程中获取输入,但如果您工作正确,您可以获得退出值:) 这适用于任何不会给您提供复杂响应的东西(例如在大文件上使用 cat )

两者的区别很容易找到,例如:

command = "cat /sys" // works, exits with 1

command = "cat /init.rc"  doesnt work, exits with 0

这就是我设置它以轻松工作的方式。使用 MasterJB 提供的方法正常运行命令:

process p;
try {
    p = Runtime.getRuntime().exec(new String[]{"su", "-c", "/system/bin/sh"});
    DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
    stdin.writeBytes(command); 
    stdin.writeBytes("echo $?\n"); 

    DataInputStream stdout = new DataInputStream(p.getInputStream());
    byte[] buffer = new byte[4096];
    int read = 0;
    String out = new String();

    while(true){
        read = stdout.read(buffer);
        out += new String(buffer, 0, read);
        if(read<4096){
            break;
        }
    // here is where you catch the error value

    int len = out.length();
    char suExitValue = out.charAt(len-2);
    Toast.makeText(getApplicationContext(), String.valueOf(suExitValue), Toast.LENGTH_SHORT).show();

    return0or1(Integer.valueOf(suExitValue), command); // 0 or 1 Method
    // end catching exit value                            
}
} catch (IOException e) {
    // TODO Auto-generated catch block
    e.printStackTrace();
}

我还发现创建“0 或 1”方法来返回发生的事情更容易。在这个例子中,它作为吐司被触发。您可能还想测试 char 是否实际上是一个整数,因为有些命令没有给出任何退出值(很奇怪,我知道。一个实例是 ls /sys,当通过 su 终端运行时,它返回一个空白退出值。)

String return0or1 (int returnValue, String command){
String message = command + " - Cannot get return value.";

if (returnValue == 0){
    message = command + " - successful.";
    return message;
}
if (returnValue == 1){
    message = command + " - failed.";
    return message;
}
return message;
}

通过一些研究,您几乎可以将任何退出值与适当的响应相匹配,只需正确捕获它们:)

这些方法只返回命令是否运行(0),但如果它得到一个双字符或三字符的退出代码,最后一个数字可能在失败时为 0(即当退出值为 10 时),所以这在大多数情况下都有效,但需要扩展以捕获双倍和三倍值。

于 2012-04-09T19:02:29.320 回答