在su进程中读取命令输出

13

首先,我将介绍我的情况。我需要在我的安卓应用中执行“su”命令,并且它可以正常工作。然后我需要执行“ls”命令并读取其输出。我通过从“su”进程获取输出流并将我的命令写入其中来实现。

问题来了,如何读取“ls”进程的输出?我只有“su”进程对象。从中获取输入流没有任何作用,因为“su”不会写任何东西。但是“ls”会写入输出消息,而我不知道如何访问它的输出消息。

我已经搜索了许多网站,但是没有找到任何解决方案。也许有人能帮我 :)

致敬

3个回答

24

好的,我已经找到了一个解决方案。它应该是这样的:

Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
DataOutputStream stdin = new DataOutputStream(p.getOutputStream());
//from here all commands are executed with su permissions
stdin.writeBytes("ls /data\n"); // \n executes the command
InputStream stdout = p.getInputStream();
byte[] buffer = new byte[BUFF_LEN];
int read;
String out = new String();
//read method will wait forever if there is nothing in the stream
//so we need to read it in another way than while((read=stdout.read(buffer))>0)
while(true){
    read = stdout.read(buffer);
    out += new String(buffer, 0, read);
    if(read<BUFF_LEN){
        //we have read everything
        break;
    }
}
//do something with the output

希望这对某些人有所帮助


4
如果系统调用没有返回任何内容,读取(read())的调用将被阻塞。在这种特定情况下,“ls”应该总是会返回一些东西,但请记住其他命令可能会导致阻塞。 - pmont
1
@glodos 有没有一种方法可以不指定BUFF_LEN来完成这个操作?也就是说,一次读取一行,直到没有更多的行为止? - David Doria
嗨,我已经成功地使用了你的示例,但是我在Android Nougat上无法使其工作。有人对此有什么想法吗? - zeroprobe
调用Runtime.getRuntime().exec("su"); 对我来说似乎已经足够了,因为su二进制文件很可能会自己启动一个shell。 - Kozuch

5
public String ls () {
    Class<?> execClass = Class.forName("android.os.Exec");
    Method createSubprocess = execClass.getMethod("createSubprocess", String.class, String.class, String.class, int[].class);
    int[] pid = new int[1];
    FileDescriptor fd = (FileDescriptor)createSubprocess.invoke(null, "/system/bin/ls", "/", null, pid);

    BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(fd)));
    String output = "";
    try {
        String line;
        while ((line = reader.readLine()) != null) {
            output += line + "\n";
        }
    }
    catch (IOException e) {}
    return output;
}

请查看此处提到的代码:

如何在Android应用程序中运行终端命令?


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);
}

参考资料

这个代码 GScript


这段代码不会起作用。没有像“android.os.Exec”这样的类。我已经读到它在Froyo及更高版本中不再起作用了。 - glodos
我尝试过类似的方法,但在命令前加上"su"后,系统会在每次执行时要求权限,这与之前的操作有所不同(例如列出另一个目录)。我需要做的就是仅获取一次"su"命令的权限(用户点击记住并且系统不再询问),然后在此进程中执行我的命令并读取它们的输出。明确一下:1.创建su进程。2.将"ls"命令写入输出流(ls以su权限执行)。3.读取"ls"输出。我无法实现第3点。 - glodos

3

我修改了@glodos的被接受的答案,针对以下问题进行了修改:

  1. the streams are closed, otherwise the exec process hangs forever, on the opened stream. If you execute ps in shell (ie adb shell) after several executions then you'll see several su processes alive. They needs to be properly terminated.
  2. added waitFor() to make sure the process is terminated.
  3. Added handling for read=-1, now commands with empty stdout can be executed. Previously they crashed on new String(buffer, 0, read)
  4. Using StringBuffer for more efficient strings handling.

    private String execCommand(String cmd) throws IOException, InterruptedException {
        Process p = Runtime.getRuntime().exec(new String[]{"su", "-c", "system/bin/sh"});
        DataOutputStream stdout = new DataOutputStream(p.getOutputStream());
    
        stdout.writeBytes(cmd);
        stdout.writeByte('\n');
        stdout.flush();
        stdout.close();
    
        BufferedReader stdin = new BufferedReader(new InputStreamReader(p.getInputStream()));
        char[] buffer = new char[1024];
        int read;
        StringBuffer out = new StringBuffer();
    
        while((read = stdin.read(buffer)) > 0) {
            out.append(buffer, 0, read);
        }
        stdin.close();
        p.waitFor();
        return out.toString();
    }
    
一些荣誉归功于@Sherif elKhatib))

很遗憾,这对我不起作用... 我就是没有得到任何结果。 - phrogg

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接