0%

如何在代码里调用执行命令行(有可能持续更新)

前言

在电脑上可以打开 cmd/powershell/iTerm2/Git Bash 进行命令行的操作,但是如何用其他语言比如 Python/Java 执行命令行呢?
这么做当然不是闲的蛋疼,而是有些工作的确依赖这样的实现。

Java/Kotlin

Java 和 Kotlin 本质上都是运行在 jvm 虚拟机上,都是调用 Runtime 接口提供的方法。以下以 Java为例。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
public class CmdShell {
public static void main(String[] args) {
run();
}

private static void run() {
String os = System.getProperty("os.name").toLowerCase();
System.out.println(os);
if (os.toLowerCase().contains("mac")) {
runOnMac("ls");
runOnMac("ls -hl");
runOnMac("adb devices");
} else if (os.contains("windows")) {
runOnWindows("dir");
runOnWindows("git log");
}
}

private static void runOnWindows(String cmd) {
Runtime runtime = Runtime.getRuntime();
String realCmd = "cmd /c " + cmd;
try {
Process p = runtime.exec(realCmd);
String result = StreamUtil.StreamToStringWithReader(p.getInputStream());
String output = String.format("execute cmd : %s and result is \n\n%s ", cmd, result);
System.out.println(output);
} catch (IOException e) {
e.printStackTrace();
}
}

private static void runOnMac(String cmd) {
Runtime runtime = Runtime.getRuntime();
try {
Process p = runtime.exec(cmd);
String result = StreamUtil.StreamToStringWithReader(p.getInputStream());
String output = String.format("execute cmd : \" %s \" and result is \n\n%s ", cmd, result);
System.out.println(output);
} catch (IOException e) {
e.printStackTrace();
}
}
}

需要注意的是

  • Mac Os X 和 Windows 上对命令行执行机制有些区别,因此需要按平台做一下区分
  • exec 有一系列的重构方法可以调用

Android

Android 应用层的实现,本质上还是 Java 或 Kotlin,因此代码实现基本和上面👆一致。但是本质上执行命令的还是内核,因此需要执行某些命令是需要权限的,具体权限可能和厂商对 ROM 的实现有关。一些比较有用的命令还是可以执行的比如,pwd,ifconfig,ping,但是 ls 命令是需要权限的(估计 root 之后的手机是可以的,2019 年想 root 一个手机忽然发现已经很难了 😢)。借助 ping 命令可以做网络相关的一些事情。

Python

1
2
3
4
5
6
7
8
9
10
11
12
import os
def os_open():
"""
popen方法通过p.read()获取终端输出,而且popen需要关闭close().
当执行成功时,close()不返回任何值,失败时,close()返回系统返回值..
可见它获取返回值的方式和os.system不同
强调的一点是,不支持参数,不支持管道
:return:
"""
out = os.popen("dir")
print(out.read())
out.close()

popen返回的是一个file对象,跟open打开文件一样操作了,r是以读的方式打开. 用 python 执行命令行,最有意思的实现莫过于之前很火爆的微信小程序游戏《跳一跳》的 刷分程序。就是通过分析图片计算位置,然后在程序内调用 adb shell swipe x,y 命令实现的

gralde

这里的 gradle 指工程构建过程的 build.gralde 或 customXXX.gradle 脚本文件。在这些脚本文件中,其实也是可以执行命令行的。

1
2
3
4
5
6
7
8
9
10
11
12
13
def getGitBranch() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine "git"
args "rev-parse", "--abbrev-ref", "HEAD"
standardOutput = stdout
}
return stdout.toString().trim()
}

ext {
getGitBranch = this.&getGitBranch
}

这样就可以使用 gitGitBranch 方法获取当前工程的 git 分支了。可以看到最核心的实现,就是把要执行的命令写到 exec 的闭包当中,然后用一个 OutputStream 的装饰器接收一下输出就可以了。当然,如果你要执行的命令,不关注结果,输出就可以忽略了。

groovy

我们知道 groovy 编译后的字节码也是在 JVM 虚拟机上执行。只是他作为一种动态类型的语言,使用起来比较灵活。在 groovy 中执行命令行和 gradle 非常相似.

1
2
3
4
5
6
7
8
9
10
11
class GitTool {
static String getGitBranch(Project project) {
def out = new ByteArrayOutputStream()
project.exec {
it.commandLine("git")
it.args("rev-parse", "--short", "HEAD")
it.standardOutput = out
}
return out.toString().trim()
}
}

你可以理解为把闭包的实现给展开了。

JavaScript

关于 JavaScript 这里简单讨论一下之前自己使用 Node 时的场景。浏览器内是如何一番场景,不太了解不做分析。

1
2
3
4
5
6
7
8
9
10
function run_cmd(cmd, args, callBack ) {
var spawn = require('child_process').spawn;
var child = spawn(cmd, args);
var resp = "";

child.stdout.on('data', function (buffer) { resp += buffer.toString() });
child.stdout.on('end', function() { callBack (resp) });
}

run_cmd( "ls", ["-l"], function(text) { console.log (text) });

run_cmd 方法定义了具体实现,后续使用此方法即可。

…… may be continued

加个鸡腿呗.