Java中执行shell笔记

java

在java中执行shell有好几种方式:第一种(exec)方式一

  1. public

    static

    synchronized

    void runshell2()

  2. {

  3.    File superuser = new File("/system/bin/superuser");

  4.  

  5.    if (superuser.exists())

  6.    {

  7.       // return device to original state

  8.       Process process;

  9.       try

  10.       {

  11.          process = Runtime.getRuntime().exec("superuser");

  12.          DataOutputStream os = new DataOutputStream(process.getOutputStream());

  13.          os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system\n");

  14.          os.writeBytes("busybox cp /system/bin/superuser /system/bin/su\n");

  15.          os.writeBytes("busybox chown 0:0 /system/bin/su\n");

  16.          os.writeBytes("chmod 4755 /system/bin/su\n");

  17.          os.writeBytes("rm /system/bin/superuser\n");

  18.          os.writeBytes("/system/bin/monkey -v 100\n");

  19.          os.writeBytes("exit\n");

  20.          os.flush();

  21.       } catch (Exception e)

  22.       {

  23.          // TODO Auto-generated catch block

  24.          e.printStackTrace();

  25.       }

  26.    }

  27. }

第一种(exec)方式二:

  1. public

    static

    synchronized

    void runshell3()

  2. {

  3.    String cmd = "sendkey 3 2\n";

  4.    try

  5.    {

  6.       Process exeEcho = Runtime.getRuntime().exec("su");

  7.       exeEcho.getOutputStream().write(cmd.getBytes());

  8.       exeEcho.getOutputStream().flush();

  9.    } catch (IOException e)

  10.    {

  11.       //showMessage("Excute exception: " + e.getMessage());

  12.       e.printStackTrace();

  13.    }

  14. }

第二种方式一:

  1. public

    static

    synchronized

    void runShell() {

  2.    ProcessBuilder pb = new ProcessBuilder("/system/bin/sh");

  3.    // java.lang.ProcessBuilder: Creates operating system processes.

  4.    pb.directory(new File("/system/bin"));// 设置shell的当前目录。

  5.    try {

  6.       Process proc = pb.start();

  7.       // 获取输入流,可以通过它获取SHELL的输出。

  8.       BufferedReader in = new BufferedReader(new InputStreamReader(proc.getInputStream()));

  9.       BufferedReader err = new BufferedReader(new InputStreamReader(proc.getErrorStream()));

  10.       // 获取输出流,可以通过它向SHELL发送命令。

  11.       PrintWriter out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(proc.getOutputStream())), true);

  12.       out.println("pwd");

  13.       out.println("su root");// 执行这一句时会弹出对话框(以下程序要求授予最高权限...),要求用户确认。

  14.       // out.println("cat /proc/version");

  15.       // out.println("monkey -v 500");

  16.       // out.println("cd /data/data");//这个目录在系统中要求有root权限才可以访问的。

  17.       // out.println("ls -l");//这个命令如果能列出当前安装的APK的数据文件存放目录,就说明我们有了ROOT权限。

  18.       out.println("exit");

  19.       // proc.waitFor();

  20.       String line;

  21.       while ((line = in.readLine()) != null) {

  22.          System.out.println(line); // 打印输出结果

  23.       }

  24.       while ((line = err.readLine()) != null) {

  25.          System.out.println(line); // 打印错误输出结果

  26.       }

  27.       in.close();

  28.       out.close();

  29.       proc.destroy();

  30.    } catch (Exception e) {

  31.       System.out.println("exception:" + e);

  32.    }

  33. }

第二种方式二:

  1. /**

  2.     * 执行一个shell命令,并返回字符串值

  3.     *

  4.     * @param cmd

  5.     * 命令名称&参数组成的数组(例如:{"/system/bin/cat", "/proc/version"})

  6.     * @param workdirectory

  7.     * 命令执行路径(例如:"system/bin/")

  8.     * @return 执行结果组成的字符串

  9.     * @throws IOException

  10.     */

  11.    public

    static

    synchronized String run(String[] cmd, String workdirectory) {

  12.       StringBuffer result = new StringBuffer();

  13.       try {

  14.          ProcessBuilder builder = new ProcessBuilder(cmd);

  15.  

  16.          InputStream in = null;

  17.          // 设置一个路径(绝对路径了就不一定需要)

  18.          if (workdirectory != null) {

  19.             // 设置工作目录(同上)

  20.             builder.directory(new File(workdirectory));

  21.             // 合并标准错误和标准输出

  22.             builder.redirectErrorStream(true);

  23.             // 启动一个新进程

  24.             Process process = builder.start();

  25.  

  26.             // 读取进程标准输出流

  27.             in = process.getInputStream();

  28.             byte[] re = new

    byte[1024];

  29.             while (in.read(re) != -1) {

  30.                result = result.append(new String(re));

  31.             }

  32.          }

  33.          // 关闭输入流

  34.          if (in != null) {

  35.             in.close();

  36.          }

  37.       } catch (Exception ex) {

  38.          ex.printStackTrace();

  39.       }

  40.       return result.toString();

  41.    }

笔记:

今天使用第一种,发现了以下问题:

  1. /**

  2.  * 执行shell

  3.  *

  4.  * @return 0:成功,其它为失败。

  5.  */

  6. public

    static

    synchronized

    int runShellCmd() {

  7.    BufferedReader input = null;

  8.    PrintWriter output = null;

  9.    Process pro = null;

  10.    try {

  11.       pro = Runtime.getRuntime().exec("adb shell ");

  12.       input = new BufferedReader(new InputStreamReader(pro.getInputStream()));

  13.       pro.getOutputStream().write("pidof mediaserver\r\n".getBytes());

  14.       pro.getOutputStream().flush();

  15.       String line = input.readLine();

  16.       int pid = 0;

  17.       /**

  18.        * 按道理说直接执行命令打印是这样的:

  19.        * root@android:/ # adb shell

  20.        * root@android:/ # pidof mediaserver

  21.        * 7114

  22.        * 也就是说第三行就应该是我取到的pid值,但是实际上却是5行?

  23.        */

  24.       for (int i = 0; i < 6; i++) {

  25.          Log.e(TAG , i + " line is " + line);

  26.          pid = toInt(line, 0);

  27.          if (pid > 0)

  28.             break;

  29.          line = input.readLine();

  30.       }

  31.       Log.e(TAG, "pid:" + pid);

  32.       /**

  33.        * 实际打印如下:

  34.        * E/MainActivity( 7036): 0 line is pidof mediaserver

  35.        * E/MainActivity( 7036): 1 line is

  36.        * E/MainActivity( 7036): 2 line is root@android:/ # pidof mediaserver

  37.        * E/MainActivity( 7036): 3 line is

  38.        * E/MainActivity( 7036): 4 line is 6946

  39.        * E/MainActivity( 7036): pid:6946

  40.        * 为什么会多出2个空行??

  41.        */

  42.       if (pid == 0) {

  43.          throw

    new IOException("not find mediaserver process!");

  44.       }

  45.       String killCmd = String.format("kill -9 %d\r\n", pid);

  46.       /**

  47.        * 直接这么使用不行的,不知道什么原因,执行结果死活不对。

  48.        */

  49.       pro.getOutputStream().write(killCmd.getBytes());

  50.       pro.getOutputStream().flush();

  51.  

  52.       /**

  53.        * 再一次这么重开就ok了,谁能告诉我原因?

  54.        */

  55.       pro.destroy();

  56.       pro = null;

  57.       pro = Runtime.getRuntime().exec("adb shell ");

  58.       pro.getOutputStream().write(killCmd.getBytes());

  59.       pro.getOutputStream().flush();

  60.  

  61.  

  62.    } catch (IOException ex) {

  63.       ex.printStackTrace();

  64.       return -1;

  65.    } finally {

  66.       try {

  67.          if (input != null) {

  68.             input.close();

  69.          }

  70.          if (output != null) {

  71.             output.close();

  72.          }

  73.       } catch (IOException e) {

  74.          e.printStackTrace();

  75.       }

  76.       if (pro != null) {

  77.          pro.destroy();

  78.          pro = null;

  79.       }

  80.    }

  81.    return 0;

  82. }

我去看看源码是怎么样的!

Runtime的exec最终调用的是ProcessManager,代码如下所示:

  1. /**

  2.  * Executes the specified command and its arguments in a separate native

  3.  * process. The new process uses the environment provided in {@code envp}

  4.  * and the working directory specified by {@code directory}.

  5.  *

  6.  * @param progArray

  7.  * the array containing the program to execute as well as any

  8.  * arguments to the program.

  9.  * @param envp

  10.  * the array containing the environment to start the new process

  11.  * in.

  12.  * @param directory

  13.  * the directory in which to execute the program. If {@code null},

  14.  * execute if in the same directory as the parent process.

  15.  * @return the new {@code Process} object that represents the native

  16.  * process.

  17.  * @throws IOException

  18.  * if the requested program can not be executed.

  19.  * @throws SecurityException

  20.  * if the current {@code SecurityManager} disallows program

  21.  * execution.

  22.  * @see SecurityManager#checkExec

  23.  * @since Android 1.0

  24.  */

  25. public Process exec(String[] progArray, String[] envp, File directory) throws IOException {

  26.     // BEGIN android-changed: push responsibility for argument checking into ProcessManager

  27.     return ProcessManager.getInstance().exec(progArray, envp, directory, false);

  28.     // END android-changed

  29. }

ProcessManager的exec代码如下:

  1. /**

  2.  * Map from pid to Process. We keep weak references to the Process objects

  3.  * and clean up the entries when no more external references are left. The

  4.  * process objects themselves don't require much memory, but file

  5.  * descriptors (associated with stdin/out/err in this case) can be

  6.  * a scarce resource.

  7.  */

  8. private

    final Map<Integer, ProcessReference> processReferences

  9.         = new HashMap<Integer, ProcessReference>();

  10. /**

  11.  * Executes a process and returns an object representing it.

  12.  */

  13. Process exec(String[] taintedCommand, String[] taintedEnvironment, File workingDirectory,

  14.         boolean redirectErrorStream) throws IOException {

  15.     // Make sure we throw the same exceptions as the RI.

  16.     if (taintedCommand == null) {

  17.         throw

    new NullPointerException();

  18.     }

  19.     if (taintedCommand.length == 0) {

  20.         throw

    new IndexOutOfBoundsException();

  21.     }

  22.  

  23.     // Handle security and safety by copying mutable inputs and checking them.

  24.     String[] command = taintedCommand.clone();

  25.     String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null;

  26.     SecurityManager securityManager = System.getSecurityManager();

  27.     if (securityManager != null) {

  28.         securityManager.checkExec(command[0]);//权限检查

  29.     }

  30.     // Check we're not passing null Strings to the native exec.

  31.     for (String arg : command) {

  32.         if (arg == null) {

  33.             throw

    new NullPointerException();

  34.         }

  35.     }

  36.     // The environment is allowed to be null or empty, but no element may be null.

  37.     if (environment != null) {

  38.         for (String env : environment) {

  39.             if (env == null) {

  40.                 throw

    new NullPointerException();

  41.             }

  42.         }

  43.     }

  44.  

  45.     FileDescriptor in = new FileDescriptor();

  46.     FileDescriptor out = new FileDescriptor();

  47.     FileDescriptor err = new FileDescriptor();

  48.  

  49.     String workingPath = (workingDirectory == null)

  50.             ? null

  51.             : workingDirectory.getPath();

  52.  

  53.     // Ensure onExit() doesn't access the process map before we add our

  54.     // entry.

  55.     synchronized (processReferences) {

  56.         int pid;

  57.         try {

  58.            /**

  59.             * 调用exec函数

  60.             */

  61.             pid = exec(command, environment, workingPath, in, out, err, redirectErrorStream);

  62.         } catch (IOException e) {

  63.             IOException wrapper = new IOException("Error running exec()."

  64.                     + " Command: " + Arrays.toString(command)

  65.                     + " Working Directory: " + workingDirectory

  66.                     + " Environment: " + Arrays.toString(environment));

  67.             wrapper.initCause(e);

  68.             throw wrapper;

  69.         }

  70.         /**

  71.          * 新建一个进程实现。

  72.          */

  73.         ProcessImpl process = new ProcessImpl(pid, in, out, err);

  74.         /**

  75.          * 创建一个进程引用。

  76.          */

  77.         ProcessReference processReference

  78.                 = new ProcessReference(process, referenceQueue);

  79.  

  80.         /**

  81.          * 加入到全局进程引用map中。

  82.          */

  83.         processReferences.put(pid, processReference);

  84.  

  85.         /*

  86.          * This will wake up the child monitor thread in case there

  87.          * weren't previously any children to wait on.

  88.          */

  89.         processReferences.notifyAll();

  90.  

  91.         return process;

  92.     }

  93. }

本地exec的原型:

  1. /**

  2.  * Executes a native process. Fills in in, out, and err and returns the

  3.  * new process ID upon success.

  4.  */

  5. static

    native

    int exec(String[] command, String[] environment,

  6.         String workingDirectory, FileDescriptor in, FileDescriptor out,

  7.         FileDescriptor err, boolean redirectErrorStream) throws IOException;

对应的native文件为:

  1. //Android 4.0.3在

  2. ./libcore/luni/src/main/native/java_lang_ProcessManager.cpp

  3. //Android 2.2在

  4. ./dalvik/libcore/luni-kernel/src/main/native/java_lang_ProcessManager.cpp

  5. //源码为:

  6. /**

  7.  * Converts Java String[] to char** and delegates to executeProcess().

  8.  */

  9. static pid_t java_lang_ProcessManager_exec(

  10.         JNIEnv* env, jclass clazz, jobjectArray javaCommands,

  11.         jobjectArray javaEnvironment, jstring javaWorkingDirectory,

  12.         jobject inDescriptor, jobject outDescriptor, jobject errDescriptor,

  13.         jboolean redirectErrorStream) {

  14.  

  15.     // Copy commands into char*[].

  16.     char** commands = convertStrings(env, javaCommands);

  17.  

  18.     // Extract working directory string.

  19.     const

    char* workingDirectory = NULL;

  20.     if (javaWorkingDirectory != NULL) {

  21.         workingDirectory = env->GetStringUTFChars(javaWorkingDirectory, NULL);

  22.     }

  23.  

  24.     // Convert environment array.

  25.     char** environment = convertStrings(env, javaEnvironment);

  26.  

  27.     //关键就这一行.

  28.     pid_t result = executeProcess(

  29.             env, commands, environment, workingDirectory,

  30.             inDescriptor, outDescriptor, errDescriptor, redirectErrorStream);

  31.  

  32.     // Temporarily clear exception so we can clean up.

  33.     jthrowable exception = env->ExceptionOccurred();

  34.     env->ExceptionClear();

  35.  

  36.     freeStrings(env, javaEnvironment, environment);

  37.  

  38.     // Clean up working directory string.

  39.     if (javaWorkingDirectory != NULL) {

  40.         env->ReleaseStringUTFChars(javaWorkingDirectory, workingDirectory);

  41.     }

  42.  

  43.     freeStrings(env, javaCommands, commands);

  44.  

  45.     // Re-throw exception if present.

  46.     if (exception != NULL) {

  47.         if (env->Throw(exception) < 0) {

  48.             LOGE("Error rethrowing exception!");

  49.         }

  50.     }

  51.  

  52.     return result;

  53. }

看看executeProcess接口,其实源码注释写的很清楚。

  1. /** Executes a command in a child process. */

  2. static pid_t executeProcess(JNIEnv* env, char** commands, char** environment,

  3.         const

    char* workingDirectory, jobject inDescriptor,

  4.         jobject outDescriptor, jobject errDescriptor,

  5.         jboolean redirectErrorStream) {

  6.     int i, result, error;

  7.  

  8.     // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.

  9.     int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };

  10.     for (i = 0; i < PIPE_COUNT; i++) {

  11.         if (pipe(pipes + i * 2) == -1) {

  12.             jniThrowIOException(env, errno);

  13.             closePipes(pipes, -1);

  14.             return -1;

  15.         }

  16.     }

  17.     int stdinIn = pipes[0];

  18.     int stdinOut = pipes[1];

  19.     int stdoutIn = pipes[2];

  20.     int stdoutOut = pipes[3];

  21.     int stderrIn = pipes[4];

  22.     int stderrOut = pipes[5];

  23.     int statusIn = pipes[6];

  24.     int statusOut = pipes[7];

  25.  

  26.     pid_t childPid = fork();

  27.  

  28.     // If fork() failed...

  29.     if (childPid == -1) {

  30.         jniThrowIOException(env, errno);

  31.         closePipes(pipes, -1);

  32.         return -1;

  33.     }

  34.  

  35.     // If this is the child process...

  36.     if (childPid == 0) {

  37.         /*

  38.          * Note: We cannot malloc() or free() after this point!

  39.          * A no-longer-running thread may be holding on to the heap lock, and

  40.          * an attempt to malloc() or free() would result in deadlock.

  41.          */

  42.  

  43.         // Replace stdin, out, and err with pipes.

  44.         dup2(stdinIn, 0);

  45.         dup2(stdoutOut, 1);

  46.         if (redirectErrorStream) {

  47.             dup2(stdoutOut, 2);

  48.         } else {

  49.             dup2(stderrOut, 2);

  50.         }

  51.  

  52.         // Close all but statusOut. This saves some work in the next step.

  53.         closePipes(pipes, statusOut);

  54.  

  55.         // Make statusOut automatically close if execvp() succeeds.

  56.         fcntl(statusOut, F_SETFD, FD_CLOEXEC);

  57.  

  58.         // Close remaining open fds with the exception of statusOut.

  59.         closeNonStandardFds(statusOut);

  60.  

  61.         // Switch to working directory.

  62.         if (workingDirectory != NULL) {

  63.             if (chdir(workingDirectory) == -1) {

  64.                 goto execFailed;

  65.             }

  66.         }

  67.  

  68.         // Set up environment.

  69.         if (environment != NULL) {

  70.             environ = environment;

  71.         }

  72.  

  73.         // Execute process. By convention, the first argument in the arg array

  74.         // should be the command itself. In fact, I get segfaults when this

  75.         // isn't the case.

  76.         execvp(commands[0], commands);

  77.  

  78.         // If we got here, execvp() failed or the working dir was invalid.

  79.         execFailed:

  80.             error = errno;

  81.             write(statusOut, &error, sizeof(int));

  82.             close(statusOut);

  83.             exit(error);

  84.     }

  85.  

  86.     // This is the parent process.

  87.  

  88.     // Close child's pipe ends.

  89.     close(stdinIn);

  90.     close(stdoutOut);

  91.     close(stderrOut);

  92.     close(statusOut);

  93.  

  94.     // Check status pipe for an error code. If execvp() succeeds, the other

  95.     // end of the pipe should automatically close, in which case, we'll read

  96.     // nothing.

  97.     int count = read(statusIn, &result, sizeof(int));

  98.     close(statusIn);

  99.     if (count > 0) {

  100.         jniThrowIOException(env, result);

  101.  

  102.         close(stdoutIn);

  103.         close(stdinOut);

  104.         close(stderrIn);

  105.  

  106.         return -1;

  107.     }

  108.  

  109.     // Fill in file descriptor wrappers.

  110.     jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);

  111.     jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);

  112.     jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);

  113.  

  114.     return childPid;

  115. }

至此,Runtime的exec就全部结束了。如果对下面的fork,execvp这2个函数不了解。建议看看APU。

 

最后来看看ProcessBuilder类的实现:

  1. /**

  2.  * Starts a new process based on the current state of this process builder.

  3.  *

  4.  * @return the new {@code Process} instance.

  5.  * @throws NullPointerException

  6.  * if any of the elements of {@link #command()} is {@code null}.

  7.  * @throws IndexOutOfBoundsException

  8.  * if {@link #command()} is empty.

  9.  * @throws SecurityException

  10.  * if {@link SecurityManager#checkExec(String)} doesn't allow

  11.  * process creation.

  12.  * @throws IOException

  13.  * if an I/O error happens.

  14.  */

  15. public Process start() throws IOException {

  16.     // BEGIN android-changed: push responsibility for argument checking into ProcessManager

  17.     String[] cmdArray = command.toArray(new String[command.size()]);

  18.     String[] envArray = new String[environment.size()];

  19.     int i = 0;

  20.     for (Map.Entry<String, String> entry : environment.entrySet()) {

  21.         envArray[i++] = entry.getKey() + "=" + entry.getValue(); //$NON-NLS-1$

  22.     }

  23.     //和Runtime.exec的一样。

  24.     return ProcessManager.getInstance().exec(cmdArray, envArray, directory, redirectErrorStream);

  25.     // END android-changed

  26. }

殊路同归!!!哈。

 

 

 

 

以上是 Java中执行shell笔记 的全部内容, 来源链接: utcz.com/z/390763.html

回到顶部