Java中执行shell笔记

本文介绍了在Java中执行Shell命令的多种方式,包括使用Runtime.exec和ProcessBuilder,并详细解析了每种方法的具体实现及注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载: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\n");
         os.writeBytes("busybox cp /system/bin/superuser /system/bin/su\n");
         os.writeBytes("busybox chown 0:0 /system/bin/su\n");
         os.writeBytes("chmod 4755 /system/bin/su\n");
         os.writeBytes("rm /system/bin/superuser\n");
         os.writeBytes("/system/bin/monkey -v 100\n");
         os.writeBytes("exit\n");
         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\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.          InputStream in = null;
  16.          // 设置一个路径(绝对路径了就不一定需要)
  17.          if (workdirectory != null) {
  18.             // 设置工作目录(同上)
  19.             builder.directory(new File(workdirectory));
  20.             // 合并标准错误和标准输出
  21.             builder.redirectErrorStream(true);
  22.             // 启动一个新进程
  23.             Process process = builder.start();
  24.             // 读取进程标准输出流
  25.             in = process.getInputStream();
  26.             byte[] re = new byte[1024];
  27.             while (in.read(re) != -1) {
  28.                result = result.append(new String(re));
  29.             }
  30.          }
  31.          // 关闭输入流
  32.          if (in != null) {
  33.             in.close();
  34.          }
  35.       } catch (Exception ex) {
  36.          ex.printStackTrace();
  37.       }
  38.       return result.toString();
  39.    }

笔记:

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

  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.        * 再一次这么重开就ok了,谁能告诉我原因?
  53.        */
  54.       pro.destroy();
  55.       pro = null;
  56.       pro = Runtime.getRuntime().exec("adb shell ");
  57.       pro.getOutputStream().write(killCmd.getBytes());
  58.       pro.getOutputStream().flush();
  59.    } catch (IOException ex) {
  60.       ex.printStackTrace();
  61.       return -1;
  62.    } finally {
  63.       try {
  64.          if (input != null) {
  65.             input.close();
  66.          }
  67.          if (output != null) {
  68.             output.close();
  69.          }
  70.       } catch (IOException e) {
  71.          e.printStackTrace();
  72.       }
  73.       if (pro != null) {
  74.          pro.destroy();
  75.          pro = null;
  76.       }
  77.    }
  78.    return 0;
  79. }

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

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.     // Handle security and safety by copying mutable inputs and checking them.
  23.     String[] command = taintedCommand.clone();
  24.     String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null;
  25.     SecurityManager securityManager = System.getSecurityManager();
  26.     if (securityManager != null) {
  27.         securityManager.checkExec(command[0]);//权限检查
  28.     }
  29.     // Check we're not passing null Strings to the native exec.
  30.     for (String arg : command) {
  31.         if (arg == null) {
  32.             throw new NullPointerException();
  33.         }
  34.     }
  35.     // The environment is allowed to be null or empty, but no element may be null.
  36.     if (environment != null) {
  37.         for (String env : environment) {
  38.             if (env == null) {
  39.                 throw new NullPointerException();
  40.             }
  41.         }
  42.     }
  43.     FileDescriptor in = new FileDescriptor();
  44.     FileDescriptor out = new FileDescriptor();
  45.     FileDescriptor err = new FileDescriptor();
  46.     String workingPath = (workingDirectory == null)
  47.             ? null
  48.             : workingDirectory.getPath();
  49.     // Ensure onExit() doesn't access the process map before we add our
  50.     // entry.
  51.     synchronized (processReferences) {
  52.         int pid;
  53.         try {
  54.            /**
  55.             * 调用exec函数
  56.             */
  57.             pid = exec(command, environment, workingPath, in, out, err, redirectErrorStream);
  58.         } catch (IOException e) {
  59.             IOException wrapper = new IOException("Error running exec()."
  60.                     + " Command: " + Arrays.toString(command)
  61.                     + " Working Directory: " + workingDirectory
  62.                     + " Environment: " + Arrays.toString(environment));
  63.             wrapper.initCause(e);
  64.             throw wrapper;
  65.         }
  66.         /**
  67.          * 新建一个进程实现。
  68.          */
  69.         ProcessImpl process = new ProcessImpl(pid, in, out, err);
  70.         /**
  71.          * 创建一个进程引用。
  72.          */
  73.         ProcessReference processReference
  74.                 = new ProcessReference(process, referenceQueue);
  75.         /**
  76.          * 加入到全局进程引用map中。
  77.          */
  78.         processReferences.put(pid, processReference);
  79.         /*
  80.          * This will wake up the child monitor thread in case there
  81.          * weren't previously any children to wait on.
  82.          */
  83.         processReferences.notifyAll();
  84.         return process;
  85.     }
  86. }

本地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.     // Copy commands into char*[].
  15.     char** commands = convertStrings(env, javaCommands);
  16.     // Extract working directory string.
  17.     const char* workingDirectory = NULL;
  18.     if (javaWorkingDirectory != NULL) {
  19.         workingDirectory = env->GetStringUTFChars(javaWorkingDirectory, NULL);
  20.     }
  21.     // Convert environment array.
  22.     char** environment = convertStrings(env, javaEnvironment);
  23.     //关键就这一行.
  24.     pid_t result = executeProcess(
  25.             env, commands, environment, workingDirectory,
  26.             inDescriptor, outDescriptor, errDescriptor, redirectErrorStream);
  27.     // Temporarily clear exception so we can clean up.
  28.     jthrowable exception = env->ExceptionOccurred();
  29.     env->ExceptionClear();
  30.     freeStrings(env, javaEnvironment, environment);
  31.     // Clean up working directory string.
  32.     if (javaWorkingDirectory != NULL) {
  33.         env->ReleaseStringUTFChars(javaWorkingDirectory, workingDirectory);
  34.     }
  35.     freeStrings(env, javaCommands, commands);
  36.     // Re-throw exception if present.
  37.     if (exception != NULL) {
  38.         if (env->Throw(exception) < 0) {
  39.             LOGE("Error rethrowing exception!");
  40.         }
  41.     }
  42.     return result;
  43. }

看看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.     // Create 4 pipes: stdin, stdout, stderr, and an exec() status pipe.
  8.     int pipes[PIPE_COUNT * 2] = { -1, -1, -1, -1, -1, -1, -1, -1 };
  9.     for (i = 0; i < PIPE_COUNT; i++) {
  10.         if (pipe(pipes + i * 2) == -1) {
  11.             jniThrowIOException(env, errno);
  12.             closePipes(pipes, -1);
  13.             return -1;
  14.         }
  15.     }
  16.     int stdinIn = pipes[0];
  17.     int stdinOut = pipes[1];
  18.     int stdoutIn = pipes[2];
  19.     int stdoutOut = pipes[3];
  20.     int stderrIn = pipes[4];
  21.     int stderrOut = pipes[5];
  22.     int statusIn = pipes[6];
  23.     int statusOut = pipes[7];
  24.     pid_t childPid = fork();
  25.     // If fork() failed...
  26.     if (childPid == -1) {
  27.         jniThrowIOException(env, errno);
  28.         closePipes(pipes, -1);
  29.         return -1;
  30.     }
  31.     // If this is the child process...
  32.     if (childPid == 0) {
  33.         /*
  34.          * Note: We cannot malloc() or free() after this point!
  35.          * A no-longer-running thread may be holding on to the heap lock, and
  36.          * an attempt to malloc() or free() would result in deadlock.
  37.          */
  38.         // Replace stdin, out, and err with pipes.
  39.         dup2(stdinIn, 0);
  40.         dup2(stdoutOut, 1);
  41.         if (redirectErrorStream) {
  42.             dup2(stdoutOut, 2);
  43.         } else {
  44.             dup2(stderrOut, 2);
  45.         }
  46.         // Close all but statusOut. This saves some work in the next step.
  47.         closePipes(pipes, statusOut);
  48.         // Make statusOut automatically close if execvp() succeeds.
  49.         fcntl(statusOut, F_SETFD, FD_CLOEXEC);
  50.         // Close remaining open fds with the exception of statusOut.
  51.         closeNonStandardFds(statusOut);
  52.         // Switch to working directory.
  53.         if (workingDirectory != NULL) {
  54.             if (chdir(workingDirectory) == -1) {
  55.                 goto execFailed;
  56.             }
  57.         }
  58.         // Set up environment.
  59.         if (environment != NULL) {
  60.             environ = environment;
  61.         }
  62.         // Execute process. By convention, the first argument in the arg array
  63.         // should be the command itself. In fact, I get segfaults when this
  64.         // isn't the case.
  65.         execvp(commands[0], commands);
  66.         // If we got here, execvp() failed or the working dir was invalid.
  67.         execFailed:
  68.             error = errno;
  69.             write(statusOut, &error, sizeof(int));
  70.             close(statusOut);
  71.             exit(error);
  72.     }
  73.     // This is the parent process.
  74.     // Close child's pipe ends.
  75.     close(stdinIn);
  76.     close(stdoutOut);
  77.     close(stderrOut);
  78.     close(statusOut);
  79.     // Check status pipe for an error code. If execvp() succeeds, the other
  80.     // end of the pipe should automatically close, in which case, we'll read
  81.     // nothing.
  82.     int count = read(statusIn, &result, sizeof(int));
  83.     close(statusIn);
  84.     if (count > 0) {
  85.         jniThrowIOException(env, result);
  86.         close(stdoutIn);
  87.         close(stdinOut);
  88.         close(stderrIn);
  89.         return -1;
  90.     }
  91.     // Fill in file descriptor wrappers.
  92.     jniSetFileDescriptorOfFD(env, inDescriptor, stdoutIn);
  93.     jniSetFileDescriptorOfFD(env, outDescriptor, stdinOut);
  94.     jniSetFileDescriptorOfFD(env, errDescriptor, stderrIn);
  95.     return childPid;
  96. }

至此,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. }

殊路同归!!!哈。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值