Java中执行shell笔记

在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. }

殊路同归!!!哈。

 

 

 

 

posted on 2013-07-03 15:38  J.evan  阅读(17906)  评论(0编辑  收藏  举报