如何使用Java通过Windows/cygwin执行Unix命令

11

我想要实现两件事:

  1. 我正在Windows7上运行cygwin以执行我的unix shell命令,我需要编写一个Java应用程序来自动化这个过程。我已经知道如何使用Java通过“Process类”和Runtime.getRuntime().exec("cmd /c dir")使用Windows shell。我需要能够执行相同的unix命令,例如:ls -la等。我应该研究什么?

  2. 有没有一种方法可以记住shell的状态? 解释:当我使用Runtime.getRuntime().exec("cmd /c dir")时,我总是会得到我的主目录列表。如果我执行Runtime.getRuntime().exec("cmd /c cd <some-folder>"),然后再次执行Runtime.getRuntime().exec("cmd /c dir"),我仍然会得到我的主文件夹的列表。有没有办法告诉进程记住它的状态,就像普通的shell一样?


看起来Paŭlo提出的命令行似乎不起作用:

C:\cygwin\bin>bash -c ls -la
-la: ls: command not found

我在理解技术细节方面遇到了麻烦。

这是我的代码:

p = Runtime.getRuntime().exec("C:\\cygwin\\bin\\bash.exe -c ls -la");
reader2 = new BufferedReader(new InputStreamReader(p.getInputStream()));
line = reader2.readLine();

line 最终变成了 null 值。


我把这个东西添加到我的 .bash_profile 文件中:

#BASH
export BASH_HOME=/cygdrive/c/cygwin
export PATH=$BASH_HOME/bin:$PATH

我还添加了以下内容:

系统属性 -> 高级 -> 环境变量 -> 用户变量 -> 变量名称:BASH,变量值:c:\cygwin\bin

但仍然没有任何反应...

但是,如果我执行以下操作,则可以正常工作!

p = Runtime.getRuntime().exec("c:\\cygwin\\bin\\ls -la ~/\"Eclipse_Workspace/RenameScript/files copy\"");

好的,让我们看一下。如果我们给出正确的路径,直接执行 ls.exe 似乎可以工作。 - Paŭlo Ebermann
好的,那么我该如何让Bash在使用-c参数时识别它呢? - Martin Klosi
我认为在使用“-c”执行时,它不会执行您的“.bashrc”。您可以尝试使用“envp”参数设置路径以执行。 (我会在我的答案中添加这一点。) - Paŭlo Ebermann
p = Runtime.getRuntime().exec("C:/cygwin/bin/bash -c '/bin/ls -la'"); 这样也可以工作 - Martin Klosi
2个回答

11

1. 调用Unix命令:

你只需要调用你的Unix shell(例如随Cygwin提供的bash),而不是调用cmd

bash -c "ls -la"

当然,如果你的命令是一个外部程序,你可以直接调用它:

ls -la

如果从Java开始,最好使用接受字符串数组的变体,因为这样您就不需要让Java解析以查看参数的起始位置和结束位置:

Process p = 
     Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe",
                                            "-c", "ls -la"},
                               new String[]{"PATH=/cygdrive/c/cygwin/bin"});
你代码中的错误信息(ls: command not found)表明你的bash无法找到ls命令。也许你需要将其放入PATH变量中(请参见上面的Java方法)。
也许正确的目录名应该是/usr/bin而不是/cygdrive/c/cygwin/bin。(由于必须在Unix和Windows之间进行桥接,所以这里一切都有点复杂。)
可以按以下方式调用简单的ls命令:
Process p = Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\ls.exe", "-la"});

2. 调用多个命令:

在一个 shell 中调用多个命令基本上有两种方式:

  • 将它们一起传递给 shell;或者
  • 将它们交互式地传递给 shell。

对于第一种方式,只需将多个命令作为 -c 选项的参数一起传递给 shell,并用 ;\n (换行符) 分隔,像这样:

bash -c "cd /bin/ ; ls -la"

或者来自Java(调整上面的示例):

Process p = 
     Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe",
                                            "-c", "cd /bin/; ls -la"},
                               new String[]{"PATH=/cygdrive/c/cygwin/bin"});

在这里,shell会将命令行解析并作为脚本执行。如果包含多个命令,它们都将被执行,除非shell在某种情况下提前退出(比如使用exit命令)。 (我不确定Windows的cmd是否以类似的方式工作。请测试并报告。)

与其在命令行上传递bash(或cmd或您正在使用的其他shell)命令,您可以通过进程的输入流传递它们。

  • 在“输入模式”下启动的Shell(例如,在没有-c选项或shell脚本文件参数的情况下启动的Shell) 将从流中读取输入,并将第一行解释为一个或多个命令。
  • 然后它将执行此命令。如果需要,命令本身可能会从流中读取更多输入。
  • 然后Shell将读取下一行,将其解释为命令并执行。
  • (在某些情况下,Shell必须读取多行,例如对于长字符串或if或循环之类的组合命令。)
  • 这将继续进行,直到流结束(例如,在您这一侧使用stream.close())或执行显式的exit命令(或其他原因退出)。

下面是一个示例:

Process p = Runtime.getRuntime().exec(new String[]{"C:\\cygwin\\bin\\bash.exe", "-s"});
InputStream outStream = p.getInputStream(); // normal output of the shell
InputStream errStream = p.getInputStream(); // error output of the shell
// TODO: start separate threads to read these streams

PrintStream ps = new PrintStream(p.getOutputStream());
ps.println("cd /bin/");
ps.println("ls -la");
ps.println("exit");
ps.close();

请将代码放入您的问题中(有一个编辑按钮),它不适合作为评论阅读。 - Paŭlo Ebermann
另外,我认为您不需要mintty,因为您不需要终端 - 直接调用您的bash.exe即可。 - Paŭlo Ebermann
原始帖子已编辑!我也不明白如何在“StackOverflow”中回答评论。 - Martin Klosi
你的意思是编辑 .bash_profile 文件并添加每个 Unix 命令吗?!! - Martin Klosi
不,问题仍然是一个小时之前的形式。 - Paŭlo Ebermann
显示剩余9条评论

1

这里不需要使用cygwin。有几个纯Java库实现SSH协议,可以使用它们。顺便说一下,它们将解决您的第二个问题。您将打开会话并在同一会话中执行命令,因此shell状态将自动保留。

一个例子是JSch


1
SSH无法在Windows系统上执行Unix shell命令。 - Paŭlo Ebermann
jsch的javadocs无法使用,我也无法弄清楚依赖关系。你能帮我吗?我的pom.xml文件应该放什么? - Martin Klosi
@Martin:JSch不是Java中的Unix仿真,而是SSH客户端实现。您可以使用它连接到服务器(通常是另一台计算机),该服务器必须具有SSH服务器和shell。 - Paŭlo Ebermann
我可以通过使用JSch绕过在Windows上尝试运行Unix命令的所有麻烦吗?实际上,我正在尝试通过SSH访问另一台计算机并在远程计算机上运行Unix命令。我想要做的是:ssh <machine-name-here> <custom-application-here> <arguments-for-the-custom-application-here>,然后获取它在本地机器上产生的输出并将其写入文件。 - Martin Klosi
@Martin:另一方面,你可以尝试使用ssh(或c:/cygwin/bin/ssh.exe)代替bash来运行所有的示例。 - Paŭlo Ebermann
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接