如何在windows下运行.sh并从java文件关联中获取返回值

qyuhtwio  于 2021-06-29  发布在  Java
关注(0)|答案(1)|浏览(405)

背景

我正在写一个运行 .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:command= ). 我要避免准备专门的 .properties 分别用于开发环境和生产环境的文件。
我们使用java的windows版本主要是因为两个原因:1)我们从java程序调用的中间件(它只支持windows和rhel,但不支持wsl环境),2)项目成员的技能集(只有少数成员可以流利地使用windows和linux)。
我们希望尽可能避免只在开发环境中使用的额外代码,因为代码覆盖率度量和质量管理。

我所尝试和证实的

测试java程序和 .sh 在windows环境下,我决定使用wsl2。
.sh 在windows上,我设置了wsl2并修改了 .sh 文件夹。我已将以下默认值添加到键 HKEY_LOCAL_MACHINE\SOFTWARE\Classes\sh_auto_file\shell\open\command.sh 在wsl2上:

"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 从java到wsl2上的文件关联?或者,是否没有办法根据文件关联启动程序并在java中获取返回值?
请注意,您可以通过以下方式启动具有文件关联的程序: java.awt.Desktop#open() ,但它没有获取返回值的方法。
还要注意,准备 .properties 像下面这样的开发环境的文件需要我们的先决条件非常艰苦的工作。

command=wsl
args[0]=C:/tmp/hoge.sh
args[1]=foo
args[2]=bar
args[3]=baz
dgiusagp

dgiusagp1#

您可以让windows和linux版本都运行bash-c“yourcommand”,以简化操作系统处理的差异。wsl安装bash.exe,用cmd.exe确认它在那里:

> where bash
C:\Windows\System32\bash.exe

如果在两个操作系统上都正确设置了路径,则可以使用“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"};

相关问题