Android上执行shell命令的总结

Android提供了两种执行shell命令的方法。

1.Runtime.getRuntime().exec(cmds)

2.ProcessBuilder(cmds)

两种方法背后的逻辑相同。都是创建一个新的进程,执行shell命令。本文以第一种方法为例。代码如下:

public class RuntimeExcUtil {

    public static int run(String[] fCmds){
        int tCode=0;
        mLogger.i("APP_SHELL", "start run");
        try {
            Process tProcess = Runtime.getRuntime().exec(fCmds);
            printError(tProcess);
            tCode = tProcess.waitFor();
            mLogger.i("APP_SHELL", "Code Result= "+tCode);
        } catch (Exception e) {
            mLogger.e("APP_SHELL", "run Failed, Error Message :"+e.getMessage());
        }
        return tCode;
    }

    public static int runWithSu(String[] fCmds){
        int tCode=0;
        mLogger.i("APP_SHELL", "start run");
        try {
            Process tProcess = Runtime.getRuntime().exec("su");
            runShellScript(tProcess,fCmds);
            printError(tProcess);
            tCode = tProcess.waitFor();
            mLogger.i("APP_SHELL", "Code Result= "+tCode);
        } catch (Exception e) {
            mLogger.e("APP_SHELL", "runWithSu Failed, Error Message :"+e.getMessage());
        }
        return tCode;
    }

    private static void printError(Process fProcess){
        StringBuilder output = new StringBuilder();
        String line;
        try(BufferedReader tReader = new BufferedReader(
                new InputStreamReader(fProcess.getErrorStream()))) {
            while ((line = tReader.readLine()) != null) {
                output.append(line).append("\n");
            }
            if (output.toString().length()>0){
                mLogger.e("APP_SHELL", "Error Output = "+output);
            }
        }catch (Exception e){
            mLogger.e("APP_SHELL", "Print Error Output Failed, Error Message :"+e.getMessage());
        }
    }

    private static void runShellScript(Process fProcess, String[] fCmds){
        try(DataOutputStream outputStream = new DataOutputStream(fProcess.getOutputStream())) {
            for (String tCmd :
                    fCmds) {
                Log.i("APP_SHELL", "Cmd :" + tCmd);
                outputStream.writeBytes(tCmd + "\n");
            }
            outputStream.flush();
            outputStream.writeBytes("exit\n");
            outputStream.flush();
        } catch (Exception e) {
            Log.e("APP_SHELL", "Run Shell Script Failed, Error Message :"+e.getMessage());
        }
    }
}

测试代码如下:

//case 1
RuntimeExcUtil.runWithSu(new String[]{"cp -a -p "+tJavaCrashLogFolderPath+" "+ tCollectFolder});
//case 2
RuntimeExcUtil.runWithSu(new String[]{"cd "+tJavaCrashLogFolderPath,"rm -f crash*"});
//case 3
RuntimeExcUtil.run(new String[]{"cd /sdcard/Download"});
//case 4
RuntimeExcUtil.run(new String[]{"cd", "/sdcard/Download"});
//case 5
RuntimeExcUtil.run(new String[]{"cp -a -p "+tJavaCrashLogFolderPath+" "+ tCollectFolder});
//case 6
RuntimeExcUtil.run(new String[]{"cp", "-a", "-p", tJavaCrashLogFolderPath,tCollectFolder});
//case 7
RuntimeExcUtil.run(new String[]{"rm -f /sdcard/crash/crash*"});
//case 8
RuntimeExcUtil.run(new String[]{"rm", "-f", "/sdcard/crash/crash*"});
Case 结果分析
1

结果:执行成功。

原因:创建su窗口,并获取上下文,将后续指令写入

DataOutputStream。不会有权限问题,不会有指令解析问题。

2

结果:同1

原因:同1

3

结果:执行失败。返回Cannot run program "cd /sdcard/Download": error=2, No such file or directory。

原因:将整个命令字符串作为一个参数传递给 RuntimeExcUtil.run() 方法。这个方法可能会将整个字符串解释为一个可执行程序的名称,而不是将其分解为命令和参数。因此,它会尝试去找一个名为 cp -a -p /sdcard/crash/ /destination_folder 的可执行程序。若找不到,则返回No such file or directory。

4

结果:执行失败。返回Cannot run program "cd": error=13, Permission denied。

原因:“cd”是用来改变当前工作目录的 shell 命令,并不是一个可执行程序。所以会得到 "Permission denied" 的错误,这是因为 Android 不允许直接执行这种命令。

5

结果:同3

原因:同3

6

结果:执行成功

原因:将指令与参数分开,方便解析。

7

结果:同3

原因:同3

8

结果:执行成功

原因:将指令与参数分开,方便解析。

上一篇:<QT基础(2)>QScrollArea使用笔记-代码


下一篇:大模型提示工程之Prompt框架和示例