2

我一直在尝试为 Android 构建终端模拟器。对此很陌生,我的想法是执行每个命令并将输出存储在一个文件中,每次执行后都会显示其内容。 伪代码:

public Boolean execCommands(String command) {
        try {
            rt = Runtime.getRuntime();
            process = rt.exec("su");
            DataOutputStream os = new DataOutputStream(process.getOutputStream());
            os.writeBytes("echo $ \""+command+ "\" >> /sdcard/Android/data/terminalemulatorlog.txt\n\n\n"); 
            /**** Note :  String command = (EditText)findViewById(R.id.command).getText().toString(); ****/
            os.flush();
            os.writeBytes("exit\n");
            os.flush();
            process.waitFor();
            }
        // Error Handling
        displayOutput(); //Loads and displays the Text File (/sdcard/Android/data/terminalemulatorlog.txt)
        return true;
        }

这段代码除了一些特殊的命令(例如'clear')外都可以工作。但我更关心的是以下问题:

  1. 每次要执行命令时,我最终都会寻求超级用户权限(第二行代码)。我想取消这个。
  2. 如果用户输入一个命令,然后输入另一个命令,
    例如:

    cd /sdcard    
    touch File.txt    
    

    File.txt是在“/”中创建的,而不是在“/sdcard”中。到目前为止,为了避免这种情况,我一直在跟踪所有的“cd”命令,以确定当前的工作目录是什么。我希望有更好的方法来解决这个问题。

如果有人可以在这里帮助我,我将不胜感激。

4

3 回答 3

2

不确定您是否仍然需要这个,但这是我一次发出多个命令而不使用“su”让它们运行的​​方式。

try {
    String[] commands = {           
            "dumpstate > /sdcard/LogFiles/dumpstate.txt",
            "dumpsys > /sdcard/LogFiles/dumpsys.txt",
            "logcat -d > /sdcard/LogFiles/log.txt",
            "cat /sdcard/LogFiles/dumpstate.txt /sdcard/LogFiles/dumpsys.txt /sdcard/LogFiles/log.txt > /sdcard/LogFiles/bugreport.rtf" };
    Process p = Runtime.getRuntime().exec("/system/bin/sh -");
    DataOutputStream os = new DataOutputStream(p.getOutputStream());
    for (String tmpCmd : commands) {
        os.writeBytes(tmpCmd + "\n");
    }
} catch (IOException e) {
    e.printStackTrace();
}
于 2012-02-02T19:50:50.703 回答
2

这有点晚了,但这里有一些方法可以做到这一点。

1)

不要使用 su 作为起点,而是使用 /system/bin/sh。

并在致电后

rt.exec("/system/bin/sh");

您应该抓住输出流和输入流以提供进一步的命令。

发出命令后,您应该回显一条像“---EOF---”这样的神奇行,并在读取该行后停止读取输入。如果你没有这个,你最终会得到 InputStream 阻塞的读取函数。

2) 将数据传送到您编写的本机进程,该进程只需将数据移动到您的 Android 应用程序,并在末尾附加一个终止字符或字符串。

我不完全确定如何做到这一点,但它与以前的方法基本相同,只是依赖于你的本机应用程序作为中间人。

这将使您接近功能正常的“终端仿真器”。

3)如果您不是真正的终端模拟器,那么没有其他方法可以做到这一点:使用打开与伪终端连接的本机应用程序。

以下是有关如何打开 pty 的一些基本信息:链接

Terminal Emulator 是一个使用这种技术的开源项目。

看看这里

于 2012-10-23T07:46:48.063 回答
0

关于问题1:

每次要执行命令时,我最终都会寻求超级用户权限(第二行代码)。我想取消这个。

感谢 Xonar 从另一个答案中提出的建议:

发出命令后,您应该回显一条像“---EOF---”这样的神奇行,并在读取该行后停止读取输入。

Kotlin 中的解决方案:

private lateinit var suProcess: Process
private lateinit var outputStream: DataOutputStream

private fun getSu(): Boolean {
    return try {
        suProcess = Runtime.getRuntime().exec("su")
        outputStream = DataOutputStream(suProcess.outputStream)
        true
    } catch (e: Exception) {
        e.printStackTrace()
        false
    }
}

private fun sudo(command: String): List<String>? {
    return try {
        outputStream.writeBytes("$command\n")
        outputStream.flush()

        outputStream.writeBytes("echo ---EOF---\n")
        outputStream.flush()

        val reader = suProcess.inputStream.bufferedReader()
        val result = mutableListOf<String>()
        while (true) {
            val line = reader.readLine()
            if (line == "---EOF---") break
            result += line
        }

        result
    } catch (e: Exception) {
        e.printStackTrace()
        null
    }
}

private fun exitTerminal() {
    try {
        outputStream.writeBytes("exit\n")
        outputStream.flush()

        suProcess.waitFor()
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        outputStream.close()
    }
}

//Activity method
override fun onDestroy() {
    super.onDestroy()

    exitTerminal()
}
于 2020-02-05T14:12:37.720 回答