java - 如何在 Windows 上通过文件关联运行 .sh 并从 Java 获取返回值
问题描述
背景
我正在编写一个 Java 程序,它运行files.sh
中指定的.properties
文件并获取其返回值。文件的路径.sh
是从以下文件中指定的.properties
:
command=C:/tmp/hoge.sh
args[0]=foo
args[1]=bar
args[2]=baz
Java 程序的读法如下:
ClassLoader cl = Thread.currentThread().getContextClassLoader();
InputStream is = cl.getResourceAsStream("resources/command.properties");
Properties prop = new Properties();
prop.load(is);
List<String> list = new ArrayList<String>();
list.add(prop.getProperty("command"));
list.add(prop.getProperty("args[0]"));
list.add(prop.getProperty("args[1]"));
list.add(prop.getProperty("args[2]"));
ProcessBuilder pb = new ProcessBuilder(list);
Process p = pb.start();
int returnValue = p.exitValue();
该程序在生产环境的 Linux 上运行。但是由于开发环境管理的原因,我们使用Windows作为开发环境。
先决条件
.sh
我们的系统上有很多.properties
文件。所以我想.properties
在开发环境和生产环境上使用相同的文件,或者至少我想使用.properties
简单的字符串替换的文件(在上面的例子中,我要做的就是替换command=C:
tocommand=
)。我想避免分别.properties
为开发环境和生产环境准备专用文件。- 我们使用 Windows 版本的 Java 主要是因为两个原因:1)我们从 Java 程序调用的中间件(它只支持 Windows 和 RHEL,但不支持 WSL 环境),以及 2)项目成员的技能集(有能流利使用 Windows 和 Linux 的只有少数成员)。
- 由于代码覆盖率测量和质量管理,我们希望尽可能避免仅在开发环境中使用的附加代码。
我已经尝试并确认的
为了在 Windows 环境下测试 Java 程序和.sh
文件,我决定使用 WSL2。
为了.sh
在 Windows 上运行,我设置了 WSL2 并修改了文件的文件关联.sh
。我已将以下默认值添加到要在 WSL2HKEY_LOCAL_MACHINE\SOFTWARE\Classes\sh_auto_file\shell\open\command
上运行的键中:.sh
"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe" "$arg=\"%1\";wsl (\"/mnt/\"+[System.Char]::ToLower($arg[0])+$arg.Substring(2,$arg.Length-2) -replace '\\', '/'); exit $LASTEXITCODE"
在命令提示符下,我已经确认我可以.sh
通过文件关联在 WSL2 上使用 Bash 运行并.sh
正确获取 的返回值。
C:\tmp>hoge.sh
C:\tmp>echo %ERRORLEVEL%
5
的内容hoge.sh
是:
sleep 1
exit 5
问题
Java 程序.sh
在 Linux 上正常运行文件。但在 Windows 上,它会导致如下错误:
Exception in thread "main" java.io.IOException: Cannot run program "C:/tmp/hoge.sh": CreateProcess error=193, %1 is not a valid Win32 application
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1048)
at org.example.Main.main(Main.java:16)
Caused by: java.io.IOException: CreateProcess error=193, %1 is not a valid Win32 application
at java.lang.ProcessImpl.create(Native Method)
at java.lang.ProcessImpl.<init>(ProcessImpl.java:386)
at java.lang.ProcessImpl.start(ProcessImpl.java:137)
at java.lang.ProcessBuilder.start(ProcessBuilder.java:1029)
... 1 more
问题
如何.sh
在 WSL2 上通过文件关联从 Java 运行?或者,有没有办法根据文件关联启动程序并在Java中获取返回值?
请注意,您可以通过 启动具有文件关联的程序java.awt.Desktop#open()
,但它没有获取返回值的方法。
另请注意,.properties
为如下所示的开发环境准备文件需要非常努力地满足我们的先决条件。
command=wsl
args[0]=C:/tmp/hoge.sh
args[1]=foo
args[2]=bar
args[3]=baz
解决方案
您可以让 Windows 和 Linux 版本都运行 BASH -c "yourcommand" 以简化操作系统处理的差异。WSL 安装 BASH.EXE,用 CMD.EXE 确认它存在:
> where bash
C:\Windows\System32\bash.exe
如果您的 PATH 在两个操作系统上都设置正确,您可以使用“bash”作为命令:
String [] cmd = new String[] { "bash" , "-c", "Insert command 'args[0]' 'args[1]' ... here"};
要使上述内容正常工作,您需要将其构造cmd[2]
为一个字符串,其中包含由空格分隔的 args[0..N] 值,并且如果它们还包含空格则用引号转义,以便为您的脚本提供正确的参数。就像是:
cmd[2] = prop.getProperty("command")+" "+ prop.getProperty("arg[0]") ...
如果您的路径未设置,您可以调整启动代码以选择bash.exe
或/bin/bash
基于os.name
:
final String OS = System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
//final boolean IS_WINDOWS = OS.startsWith("windows");
final boolean IS_LINUX = OS.startsWith("linux");
String bash = IS_LINUX ? "/bin/bash" : "bash.exe" ; // May need full path shown by where bash.EXE
String [] cmd = new String[] { bash , "-c", "Insert command args[0] args[1] ... here"};
推荐阅读
- flutter - 当包装似乎不可能时如何共享预配置的小部件
- react-native - 从对象的资产中导入图片
- excel - Adding hyperlink to a cell when the cell and the location is dynamic
- reactjs - 为什么 React 有自己的服务器?
- mongodb - Mongo 不允许保存重复数据,即使在指定为不唯一之后也是如此
- python - 验证数据并在数据无效时显示错误消息
- javascript - javascript在没有重复名称的数组中计算重复项
- android - RecyclerView 中的 Fragment 更改崩溃。我找不到解决方案
- json - 如何在jq中分组?
- c# - 关于 system.data.sqlclient.dll 的 dotnet 核心编译问题