介绍
在本文中,我们将研究如何利用Runtime
和ProcessBuilder
类来使用Java执行Shell命令和脚本。
我们使用计算机来自动化日常工作中的许多事情。系统管理员一直在运行许多命令,其中一些命令重复性很强,并且每次运行之间的更改要求最少。
自动化的过程也已经成熟。无需手动运行所有内容。使用Java,我们可以运行单个或多个Shell命令,执行Shell脚本,运行终端/命令提示符,设置工作目录以及通过核心类来操纵环境变量。
Runtime.exec()
Runtime
Java中的类是一个高级类,它存在于每个Java应用程序中。通过它,应用程序本身可以与其所在的环境进行通信。
通过该getRuntime()
方法提取与我们的应用程序关联的运行时,我们可以使用该exec()
方法直接执行命令或运行.bat
/.sh
文件。
该exec()
方法提供了一些重载的变体:
public Process exec(String command)
-执行包含在command
单独进程中的命令。public Process exec(String command, String[] envp)
-command
使用环境变量数组执行。它们以字符串数组的name=value
形式提供,遵循格式。public Process exec(String command, String[] envp, File dir)
-command
从dir
目录中执行带有指定环境变量的。public Process exec(String cmdArray[])
-以字符串数组的形式执行命令。public Process exec(String cmdArray[], String[] envp)
-用指定的环境变量执行命令。public Process exec(String cmdarray[], String[] envp, File dir)
-从dir
目录中执行带有指定环境变量的命令。
值得注意的是,这些过程是从解释器外部运行的,并且将取决于系统。
什么是另外值得注意的是之间的差异String command
和String cmdArray[]
。他们达成了同样的目标。command
无论如何,A被分解为一个数组,因此使用这两个中的任何一个都将产生相同的结果。
它是由你来决定是否exec("dir /folder")
或者exec(new String[]{"dir", "/folder"}
是你想用什么。
让我们写一些示例,看看这些重载方法之间的区别。
从字符串执行命令
让我们从这三个中最简单的方法开始:
Process process = Runtime.getRuntime().exec("ping www.stackabuse.com");
运行此代码将执行我们以字符串格式提供的命令。但是,运行此命令时看不到任何东西。
为了验证它是否正确运行,我们将保留该process
对象。让我们用一个BufferedReader
来看看发生了什么:
public static void printResults(Process process) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line = "";
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
现在,当我们在该方法之后运行此方法时exec()
,它应产生如下内容:
Pinging www.stackabuse.com [104.18.57.23] with 32 bytes of data:
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Reply from 104.18.57.23: bytes=32 time=21ms TTL=56
Ping statistics for 104.18.57.23:
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 21ms, Maximum = 21ms, Average = 21ms
请记住,在Process
浏览其他示例时,我们必须从实例中提取过程信息。
指定工作目录
如果您想从某个文件夹中运行命令,则可以执行以下操作:
Process process = Runtime.getRuntime()
.exec("cmd /c dir", null, new File("C:Users"));
//.exec("sh -c ls", null, new File("Pathname")); for non-Windows users
printResults(process);
在此,我们为该exec()
方法提供了command
,,null
用于表示新的环境变量,将new File()
设置为工作目录。
cmd /c
诸如此类的命令前的添加dir
是值得注意的。
由于我在Windows上工作,因此这将打开cmd
并/c
执行后续命令。在这种情况下为dir
。
SO用户ping
很好地回答了为什么该示例对于示例不是强制性的,但对于本示例是强制性的。
运行前面的代码将导致:
Volume in drive C has no label.
Volume Serial Number is XXXX-XXXX
Directory of C:Users
08/29/2019 05:01 PM <DIR> .
08/29/2019 05:01 PM <DIR> ..
08/18/2016 09:11 PM <DIR> Default.migrated
08/29/2019 05:01 PM <DIR> Public
05/15/2020 11:08 AM <DIR> User
0 File(s) 0 bytes
5 Dir(s) 212,555,214,848 bytes free
让我们看一下如何在几个单独的部分而不是单个String中提供上一个命令:
Process process = Runtime.getRuntime().exec(
new String[]{"cmd", "/c", "dir"},
null,
new File("C:Users"));
printResults(process);
运行这段代码还将导致:
Volume in drive C has no label.
Volume Serial Number is XXXX-XXXX
Directory of C:Users
08/29/2019 05:01 PM <DIR> .
08/29/2019 05:01 PM <DIR> ..
08/18/2016 09:11 PM <DIR> Default.migrated
08/29/2019 05:01 PM <DIR> Public
05/15/2020 11:08 AM <DIR> User
0 File(s) 0 bytes
5 Dir(s) 212,542,808,064 bytes free
最终,无论采用哪种方法-使用单个String或String数组,您输入的命令在被基础逻辑处理之前,总是会分解成一个数组。
您想使用哪一种归结为更易读的哪一种。
使用环境变量
让我们看一下如何使用环境变量:
Process process = Runtime.getRuntime().exec(
"cmd /c echo %var1%",
new String[]{"var1=value1"});
printResults(process);
我们可以在String数组中提供任意数量的环境变量。在这里,我们刚刚打印了var1
using的值echo
。
运行此代码将返回:
value1
运行.bat和.sh文件
有时,将所有内容卸载到文件中并运行该文件要比以编程方式添加所有内容容易得多。
根据您的操作系统,您可以使用.bat
或.sh
文件。让我们用内容创建一个:
echo Hello World
然后,让我们使用与以前相同的方法:
Process process = Runtime.getRuntime().exec(
"cmd /c start file.bat",
null,
new File("C:UsersUserDesktop"));
这将打开命令提示符,并.bat
在我们设置的工作目录中运行文件。
肯定地运行此代码会导致:
在处理完所有重载的exec()
签名后,让我们看一下ProcessBuilder
该类以及如何使用它执行命令。
流程构建器
ProcessBuilder
是使用该Runtime.getRuntime().exec()
方法时运行命令的基本机制:
/**
* Executes the specified command and arguments in a separate process with
* the specified environment and working directory.
*...
*/
public Process exec(String[] cmdarray, String[] envp, File dir) throws IOException {
return new ProcessBuilder(cmdarray)
.environment(envp)
.directory(dir)
.start();
}
Runtime
该类的JavaDocs
看一下如何ProcessBuilder
从exec()
方法中获取输入并运行命令,也使我们对如何使用它有了一个很好的了解。
它接受一个String[] cmdarray
,足以使其运行。另外,我们可以为其提供可选参数,例如String[] envp
和File dir
。
让我们探索这些选项。
ProcessBuilder:从字符串执行命令
cmd /c dir
在这种情况下,我们不得不将其分解,而不是能够提供单个String,例如。例如,如果我们想像以前一样列出目录中的文件,则C:/Users
可以执行以下操作:
ProcessBuilder processBuilder = new ProcessBuilder();
processBuilder.command("cmd", "/c", "dir C:Users");
Process process = processBuilder.start();
printResults(process);
要实际执行Process
,我们运行start()
命令并将返回的值分配给Process
实例。
运行此代码将产生:
Volume in drive C has no label.
Volume Serial Number is XXXX-XXXX
Directory of C:Users
08/29/2019 05:01 PM <DIR> .
08/29/2019 05:01 PM <DIR> ..
08/18/2016 09:11 PM <DIR> Default.migrated
08/29/2019 05:01 PM <DIR> Public
05/15/2020 11:08 AM <DIR> User
0 File(s) 0 bytes
5 Dir(s) 212,517,294,080 bytes free
但是,这种方法并没有比以前的方法更好。ProcessBuilder
该类的有用之处在于它是可自定义的。我们可以以编程方式设置内容,而不仅仅是通过命令。
ProcessBuilder:指定工作目录
与其通过命令提供工作目录,不如通过编程方式进行设置:
processBuilder.command("cmd", "/c", "dir").directory(new File("C:Users"));
在这里,我们将工作目录设置为与以前相同,但是我们将该定义移出了命令本身。运行此代码将提供与上一个示例相同的结果。
ProcessBuilder:环境变量
使用ProcessBuilder
s方法,很容易以形式检索环境变量列表Map
。设置环境变量也很容易,以便您的程序可以使用它们。
让我们获取当前可用的环境变量,然后添加一些变量供以后使用:
ProcessBuilder processBuilder = new ProcessBuilder();
Map<String, String> environmentVariables = processBuilder.environment();
environmentVariables.forEach((key, value) -> System.out.println(key + value));
在这里,我们将返回的环境变量打包为aMap
并对其运行forEach()
,以将值打印到控制台。
运行此代码将产生您在计算机上拥有的环境变量的列表:
DriverDataC:WindowsSystem32DriversDriverData
HerokuPathE:Heroku
ProgramDataC:ProgramData
...
现在,让我们向该列表添加一个环境变量并使用它:
environmentVariables.put("var1", "value1");
processBuilder.command("cmd", "/c", "echo", "%var1%");
Process process = processBuilder.start();
printResults(process);
运行此代码将产生:
value1
当然,一旦程序完成运行,此变量将不会保留在列表中。
ProcessBuilder:运行.bat和.sh文件
如果您想再次运行文件,我们将为ProcessBuilder
实例提供所需的信息:
processBuilder
.command("cmd", "/c", "start", "file.bat")
.directory(new File("C:UsersUserDesktop"));
Process process = processBuilder.start();
运行此代码将导致打开命令提示符并执行.bat
文件:
结论
在本文中,我们探讨了在Java中运行Shell命令的示例。我们已经使用Runtime
和ProcessBuilder
类来做到这一点。
使用Java,我们可以运行单个或多个Shell命令,执行Shell脚本,运行终端/命令提示符,设置工作目录以及通过核心类来操纵环境变量。