• Java中执行shell笔记


    转载:https://www.cnblogs.com/jevan/p/3169617.html

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

    public static synchronized void runshell2()
    {
       File superuser = new File("/system/bin/superuser");
     
       if (superuser.exists())
       {
          // return device to original state
          Process process;
          try
          {
             process = Runtime.getRuntime().exec("superuser");
             DataOutputStream os = new DataOutputStream(process.getOutputStream());
             os.writeBytes("mount -oremount,rw /dev/block/mtdblock3 /system
    ");
             os.writeBytes("busybox cp /system/bin/superuser /system/bin/su
    ");
             os.writeBytes("busybox chown 0:0 /system/bin/su
    ");
             os.writeBytes("chmod 4755 /system/bin/su
    ");
             os.writeBytes("rm /system/bin/superuser
    ");
             os.writeBytes("/system/bin/monkey -v 100
    ");
             os.writeBytes("exit
    ");
             os.flush();
          } catch (Exception e)
          {
             // TODO Auto-generated catch block
             e.printStackTrace();
          }
       }
    }

    第一种(exec)方式二:

    1. public static synchronized void runshell3()
    2. {
    3.    String cmd = "sendkey 3 2 ";
    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 ".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 ", 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. }

    殊路同归!!!哈。



  • 相关阅读:
    python
    Yii框架的学习指南(策码秀才篇)1-1 如何认识Yii framework
    yii执行流程简单介绍
    html5页面编码如何确定
    防止表单重复提交的几种策略
    2维数组排序
    YII框架开发一个项目的通用目录结构:
    phpcms添加图片投票
    windows下面apache配置虚拟目录(测试使用,发布网站不建议目录访问)
    js获取浏览器的版本代码
  • 原文地址:https://www.cnblogs.com/guanxinjing/p/9708647.html
Copyright © 2020-2023  润新知