如何 运行 .sh 并从 Java 获取 return 值,文件关联 Windows
How to run .sh and get return value from Java with file association on Windows
背景
我正在编写一个 Java 程序,该程序 运行 是 .properties
文件中指定的 .sh
文件并获取其 return 值。 .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
文件。
- 我们使用Windows版本的Java主要有两个原因:1)我们从Java程序中调用的中间件(它只支持Windows和RHEL,但不支持 WSL 环境),以及 2)项目成员的技能(只有少数成员可以流利地使用 Windows 和 Linux)。
- 由于代码覆盖率测量和质量管理,我们希望尽可能避免仅在开发环境中使用的额外代码。
我已经尝试并确认的内容
为了在Windows环境下测试Java程序和.sh
文件,我决定使用WSL2。
至 运行 .sh
Windows,我已经设置了 WSL2 并修改了 .sh
文件的文件关联。我在 WSL2 上向 HKEY_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"
在命令提示符下,我已确认我可以通过文件关联在 WSL2 上 运行 .sh
和 Bash 并获取 return 的值21=] 正确。
C:\tmp>hoge.sh
C:\tmp>echo %ERRORLEVEL%
5
hoge.sh
的内容是:
sleep 1
exit 5
问题
Java 程序 运行s .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
问题
我如何从 Java 运行 .sh
与 WSL2 上的文件关联?或者,有没有办法根据文件关联启动程序,得到Java中的return值?
请注意,您可以通过 java.awt.Desktop#open()
启动具有文件关联的程序,但它没有获取 return 值的方法。
另请注意,为下面的开发环境准备 .properties
文件需要非常努力地满足我们的先决条件。
command=wsl
args[0]=C:/tmp/hoge.sh
args[1]=foo
args[2]=bar
args[3]=baz
您可以制作 Windows 和 Linux 版本 运行 BASH -c "yourcommand" 以简化 OS 处理中的差异。 WSL 安装 BASH.EXE,用 CMD.EXE:
确认它在那里
> where bash
C:\Windows\System32\bash.exe
如果你的 PATH 在两个 OS 上都设置正确,你可以只使用“bash”作为命令:
String [] cmd = new String[] { "bash" , "-c", "Insert command 'args[0]' 'args[1]' ... here"};
要使上述工作正常,您需要将 cmd[2]
构造为一个字符串,其中包含由 space 分隔的 args[0..N] 值,如果它们还包含,则用引号转义spaces 以便为您的脚本提供正确的参数。类似于:
cmd[2] = prop.getProperty("command")+" "+ prop.getProperty("arg[0]") ...
如果您的路径未设置,您可以调整启动代码以根据 os.name
:
选择 bash.exe
或 /bin/bash
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"};
背景
我正在编写一个 Java 程序,该程序 运行 是 .properties
文件中指定的 .sh
文件并获取其 return 值。 .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
文件。 - 我们使用Windows版本的Java主要有两个原因:1)我们从Java程序中调用的中间件(它只支持Windows和RHEL,但不支持 WSL 环境),以及 2)项目成员的技能(只有少数成员可以流利地使用 Windows 和 Linux)。
- 由于代码覆盖率测量和质量管理,我们希望尽可能避免仅在开发环境中使用的额外代码。
我已经尝试并确认的内容
为了在Windows环境下测试Java程序和.sh
文件,我决定使用WSL2。
至 运行 .sh
Windows,我已经设置了 WSL2 并修改了 .sh
文件的文件关联。我在 WSL2 上向 HKEY_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"
在命令提示符下,我已确认我可以通过文件关联在 WSL2 上 运行 .sh
和 Bash 并获取 return 的值21=] 正确。
C:\tmp>hoge.sh
C:\tmp>echo %ERRORLEVEL%
5
hoge.sh
的内容是:
sleep 1
exit 5
问题
Java 程序 运行s .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
问题
我如何从 Java 运行 .sh
与 WSL2 上的文件关联?或者,有没有办法根据文件关联启动程序,得到Java中的return值?
请注意,您可以通过 java.awt.Desktop#open()
启动具有文件关联的程序,但它没有获取 return 值的方法。
另请注意,为下面的开发环境准备 .properties
文件需要非常努力地满足我们的先决条件。
command=wsl
args[0]=C:/tmp/hoge.sh
args[1]=foo
args[2]=bar
args[3]=baz
您可以制作 Windows 和 Linux 版本 运行 BASH -c "yourcommand" 以简化 OS 处理中的差异。 WSL 安装 BASH.EXE,用 CMD.EXE:
确认它在那里> where bash
C:\Windows\System32\bash.exe
如果你的 PATH 在两个 OS 上都设置正确,你可以只使用“bash”作为命令:
String [] cmd = new String[] { "bash" , "-c", "Insert command 'args[0]' 'args[1]' ... here"};
要使上述工作正常,您需要将 cmd[2]
构造为一个字符串,其中包含由 space 分隔的 args[0..N] 值,如果它们还包含,则用引号转义spaces 以便为您的脚本提供正确的参数。类似于:
cmd[2] = prop.getProperty("command")+" "+ prop.getProperty("arg[0]") ...
如果您的路径未设置,您可以调整启动代码以根据 os.name
:
bash.exe
或 /bin/bash
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"};