在 Android 中执行 Linux 终端命令
2016-03-23 16:59
537 查看
Android 作为 Linux 的分支之一,同时也支持 Linux 的一些基本终端命令,并且在 Andriod 应用中使用终端命令可以实现一些 Android API 中没有提供的功能。
Java 提供了
获得与进程标准输入相连的
获得与进程标准输出相连的
获得与进程错误输出相连的
等待进程结束,并返回进程的退出状态值,正常状态返回 0 。
强制销毁该进程。
如果需要继续输入命令,可以用
若要读取命令返回的信息,可以调用
执行完命令调用
具体代码如下:
列出当前目录下的所有文件
查看文件内容
改名或移动文件 A 为文件
拷贝文件 A 到文件 B
修改文件的权限
将字符串输出到标准输出
输出重定向,将指令的输出信息输入到文件中,会覆盖文件
配合
将指令输出添加到文件尾
Android 默认不开放
以 root 权限执行命令的过程为在应用中调用
在进程中只执行
在执行
过这个缓存,就会造成阻塞。解决办法是尽可能立即输入输出信息。
另一个问题是
在日常使用时,如果没有必要,最好是在
使用 Java 接口执行终端命令
Android 本身并没有提供执行终端命令的接口,若要在应用中执行终端命令,需要用到 Java 提供的与系统进行交互的接口。Java 提供了
ProcessBuilder().command().start()和
Runtime.getRuntime().exec()方法来执行终端命令,这两个方法都会创建一个用来执行命令的进程并返回一个
Process子类的实例。
Process类用来控制该进程或从其中获取信息。
Process 类的方法介绍
getOutputStream()
获得与进程标准输入相连的
OutputStream,用来向进程输入命令。
getInputStream()
获得与进程标准输出相连的
InputStream,用来读取进程的输出信息。
getErrorStream()
获得与进程错误输出相连的
InputStream,用来读取进程的错误信息。
waitFor()
等待进程结束,并返回进程的退出状态值,正常状态返回 0 。
destroy()
强制销毁该进程。
执行命令的具体过程
通常以Runtime.getRuntime().exec(command)方法来开启进程并执行第一个命令。
如果需要继续输入命令,可以用
getOutputStream()获取 OutputStream 向进程写入信息。
若要读取命令返回的信息,可以调用
getInputStream()获取 InputStream 读取。
执行完命令调用
waitFor()获得返回状态。
具体代码如下:
public static void executeShellCommand(String command) { Process process = null; DataOutputStream os = null; BufferedReader osReader = null; BufferedReader osErrorReader = null; try { //执行命令 process = Runtime.getRuntime().exec(command); //获得进程的输入输出流 os = new DataOutputStream(process.getOutputStream()); osReader = new BufferedReader(new InputStreamReader(process.getInputStream())); osErrorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); //输入 exit 命令以确保进程退出 os.writeBytes("exit\n"); os.flush(); int processResult; String shellMessage; String errorMessage; //获取命令执行信息 shellMessage = readOSMessage(osReader); errorMessage = readOSMessage(osErrorReader); //获得退出状态 processResult = process.waitFor(); System.out.println("processResult : " + processResult); System.out.println("shellMessage : " + shellMessage); System.out.println("errorMessage : " + errorMessage); } catch (IOException | InterruptedException e) { e.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (osReader != null) { try { osReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (osErrorReader != null) { try { osErrorReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (process != null) { process.destroy(); } } } //读取执行命令后返回的信息 private static String readOSMessage(BufferedReader messageReader) throws IOException { StringBuilder content = new StringBuilder(); String lineString; while ((lineString = messageReader.readLine()) != null) { System.out.println("lineString : " + lineString); content.append(lineString).append("\n"); } return content.toString(); }
常用 Linux 命令
记录一些常用命令。ls
列出当前目录下的所有文件
cat 文件
查看文件内容
mv 文件A 文件B
改名或移动文件 A 为文件
cp 文件A 文件B
拷贝文件 A 到文件 B
chmod {r|w|x} 文件名
修改文件的权限
echo 字符串
将字符串输出到标准输出
指令 > 文件名
输出重定向,将指令的输出信息输入到文件中,会覆盖文件
配合
echo命令可以达到写入文件的效果
指令 >> 文件名
将指令输出添加到文件尾
以 root 权限执行命令
在 Android 中执行终端命令的常见场景就是获取 root 权限。Android API 中并没有提供在非开发机上获取 root 权限的功能,所以要使用获取 root 权限的命令su。
Android 默认不开放
su命令,需要通过刷机破解才能得到使用
su命令的权限。
su命令本质是在系统目录下的一个文件,刷机破解之后使该文件拥有最高权限且可以被任何用户访问。为了安全性考虑,在破解之后还会在系统中安装一个用来管理访问
su命令的应用,通常为 SuperUser 应用。SuperUser 会在有人调用
su命令时进行拦截,并在手机上显示窗口询问是否给予本次访问权限,在点击给予权限后就可以由
su命令获得 root 权限。
以 root 权限执行命令的过程为在应用中调用
Runtime.getRuntime().exec("su")来获得一个带有 root 权限的进程,在
su命令成功后该进程中之后执行的所有命令都具有 root 权限。需要注意的是,执行
su只是使新创建的进程具有 root 权限,应用本身并不具备 root 权限。
检查是否获取到 root 权限
鉴于执行su命令有可能会被 SuperUser 等应用拒绝,有时需要检查获取权限是否被拒绝。检查的方式有两种:
在进程中只执行
su命令,然后调用
waitFor()退出获得退出状态,如果为退出状态 0 则获取成功。
在执行
su命令后执行
id命令,如果返回的信息中包含
uid=0则确认已获得 root 权限。
执行 root 命令的代码
public static void executeCommand(String command, boolean isRoot, boolean checkPermission) { Process process = null; DataOutputStream os = null; BufferedReader osReader = null; BufferedReader osErrorReader = null; try { //如果需要 root 权限则执行 su 命令,否则执行 sh 命令 process = Runtime.getRuntime().exec(isRoot ? "su" : "sh"); os = new DataOutputStream(process.getOutputStream()); osReader = new BufferedReader(new InputStreamReader(process.getInputStream())); osErrorReader = new BufferedReader(new InputStreamReader(process.getErrorStream())); //检查是否获取到 root 权限 if (checkPermission && isRoot && !checkRootPermissionInProcess(os, process.getInputStream())) { return; } os.writeBytes(command + "\n"); os.flush(); System.out.println("command : " + command); os.writeBytes("exit\n"); os.flush(); String shellMessage; int processResult; String errorMessage; shellMessage = readOSMessage(osReader); errorMessage = readOSMessage(osErrorReader); processResult = process.waitFor(); System.out.println("processResult : " + processResult); System.out.println("shellMessage : " + shellMessage); System.out.println("errorMessage : " + errorMessage); } catch (IOException | InterruptedException e) { e.printStackTrace(); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } if (osReader != null) { try { osReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (osErrorReader != null) { try { osErrorReader.close(); } catch (IOException e) { e.printStackTrace(); } } if (process != null) { process.destroy(); } } } //读取执行命令后返回的信息 private static String readOSMessage(BufferedReader messageReader) throws IOException { StringBuilder content = new StringBuilder(); String lineString; while ((lineString = messageReader.readLine()) != null) { System.out.println("lineString : " + lineString); content.append(lineString).append("\n"); } return content.toString(); } //用 id 命令检查是否获取到 root 权限 private static boolean checkRootPermissionInProcess(DataOutputStream os, InputStream osReader) throws IOException { String currentUid = readCommandResult(os, osReader, "id"); System.out.println(currentUid); if (currentUid.contains("uid=0")) { System.out.println("ROOT: Root access granted"); return true; } else { System.out.println("ROOT: Root access rejected"); return false; } } //执行一个命令,并获得该命令的返回信息 private static String readCommandResult(DataOutputStream os, InputStream in, String command) throws IOException { os.writeBytes(command + "\n"); os.flush(); return readCommandResult(in); } //读取命令返回信息 private static String readCommandResult(InputStream in) throws IOException { StringBuilder result = new StringBuilder(); // System.out.println("Before : " + in.available()); int available = 1; while (available > 0) { // System.out.println("In : " + in.available()); int b = in.read(); result.append((char) b); // System.out.println((char) b + " " + b); available = in.available(); } // System.out.println("After : " + in.available()); return result.toString(); }
踩过的一些坑
在执行cat命令读取比较大的文件时,由于返回的信息过多,可能会使读取线程阻塞。造成这种状况的原因是由于
Process类使用了标准输入输出流,而在某些平台系统中对标准输入输出流只提供了有限的缓存,所以如果你没有及时输入或输出而导致流里的信息超
过这个缓存,就会造成阻塞。解决办法是尽可能立即输入输出信息。
另一个问题是
Process并没有提供执行一个命令然后获取该命令返回信息的接口。一个不是很好的解决方法是,执行一个命令,然后调用
InputStream.read()读取一个字符,之后再调用
InputStream.available()获得该命令返回的信息长度(该长度并不一定准确),最后在读取该长度的信息。比较奇怪的是不读取一个字符
InputStream.available()返回值会一直为 0。
在日常使用时,如果没有必要,最好是在
Process中只执行一个命令,不需要返回信息则不去读取信息。
相关文章推荐
- Linux内核分析——分析system_call中断处理过程
- Linux常用命令大全(1)--系统,文件
- Linux下的AudoCAD替代软件
- CentOS下phpMyAdmin安装
- Centos7 下配置mysql5.6主从复制实例(一主两从)
- centOS下安装mysql workbench详细步骤
- Linux 如何卸载jdk
- CentOS下编译安装mysql5.6.16
- Linux学习笔记--关机和重启命令
- Linux学习--rsync+inotify实现数据实时同步
- Centos下安装mysql5.1.7
- 每天一个linux命令(50):crontab命令
- Linux清除用户登录记录和命令历史方法
- 解决Linux索引节点(inode)用满导致故障的方法
- mac 终端显示远程Linux、Unix 上的图形化程序界面
- Linux 管道通信
- Linux内核分析 笔记五 扒开系统调用的三层皮(下) ——by王玥
- CentOS6.5 登录mysql 运行SQL语句总是报错解决
- linux 网关、DNS、MTU、IP
- centos6.7rsync端与window2012服务器实时文件同步