如何使用 Java 通过 Windows/cygwin 执行 unix 命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/6751944/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to execute unix commands through Windows/cygwin using Java
提问by Martin Klosi
I am trying to accomplish two things:
我试图完成两件事:
I am running cygwin on Windows7 to execute my unix shell commands and I need to automate the process by writing a Java app. I already know how to use the windows shell through Java using the 'Process class' and
Runtime.getRuntime().exec("cmd /c dir")
. I need to be able to do the same with unix commands: i.e.:ls -la
and so forth. What should I look into?Is there a way to remember a shell's state? explanation: when I use:
Runtime.getRuntime().exec("cmd /c dir")
, I always get a listing of my home directory. If I doRuntime.getRuntime().exec("cmd /c cd <some-folder>")
and then doRuntime.getRuntime().exec("cmd /c dir")
again, I will still get the listing of my home folder. Is there a way to tell the process to remember its state, like a regular shell would?
我在 Windows7 上运行 cygwin 来执行我的 unix shell 命令,我需要通过编写 Java 应用程序来自动化该过程。我已经知道如何通过 Java 使用“进程类”和
Runtime.getRuntime().exec("cmd /c dir")
. 我需要能够对 unix 命令执行相同的操作:即:ls -la
等等。我应该看什么?有没有办法记住shell的状态?解释:当我使用: 时
Runtime.getRuntime().exec("cmd /c dir")
,我总是得到我的主目录的列表。如果我这样做Runtime.getRuntime().exec("cmd /c cd <some-folder>")
然后再做Runtime.getRuntime().exec("cmd /c dir")
一次,我仍然会得到我的主文件夹的列表。有没有办法告诉进程记住它的状态,就像一个普通的 shell 那样?
It seems that the bash
command line proposed by Pa?lo does not work:
看来bash
Pa?lo提出的命令行不行:
C:\cygwin\bin>bash -c ls -la
-la: ls: command not found
I am having trouble figuring out the technicalities.
我无法弄清楚技术细节。
This is my code:
这是我的代码:
p = Runtime.getRuntime().exec("C:\cygwin\bin\bash.exe -c ls -la");
reader2 = new BufferedReader(new InputStreamReader(p.getInputStream()));
line = reader2.readLine();
line
ends up having a null value.
line
最终有一个空值。
I added this to my .bash_profile:
我将此添加到我的 .bash_profile 中:
#BASH
export BASH_HOME=/cygdrive/c/cygwin
export PATH=$BASH_HOME/bin:$PATH
I added the following as well:
我还添加了以下内容:
System Properties -> advanced -> Environment variables -> user variebales -> variable:
BASH
, value:c:\cygwin\bin
系统属性 -> 高级 -> 环境变量 -> 用户变量 -> 变量:
BASH
,值:c:\cygwin\bin
Still nothing...
依然没有...
However, if I execute this instead, it works!
但是,如果我改为执行它,它会起作用!
p = Runtime.getRuntime().exec("c:\cygwin\bin\ls -la ~/\"Eclipse_Workspace/RenameScript/files copy\"");
回答by Pa?lo Ebermann
1. Calling unix commands:
1. 调用unix命令:
You simply need to call your unix shell (e.g. the bash delivered with cygwin) instead of cmd
.
您只需要调用您的 unix shell(例如 cygwin 提供的 bash)而不是cmd
.
bash -c "ls -la"
should do. Of course, if your command is an external program, you could simply call it directly:
应该做。当然,如果你的命令是外部程序,你可以直接调用它:
ls -la
When starting this from Java, it is best to use the variant which takes a string array, as then you don't have Java let it parse to see where the arguments start and stop:
从 Java 开始时,最好使用带有字符串数组的变体,因为这样您就没有 Java 让它解析以查看参数开始和停止的位置:
Process p =
Runtime.getRuntime().exec(new String[]{"C:\cygwin\bin\bash.exe",
"-c", "ls -la"},
new String[]{"PATH=/cygdrive/c/cygwin/bin"});
The error message in your example (ls: command not found
) seems to show that your bash can't find the ls
command. Maybe you need to put it into the PATH variable (see above for a way to do this from Java).
您的示例 ( ls: command not found
) 中的错误消息似乎表明您的 bash 找不到该ls
命令。也许您需要将其放入 PATH 变量中(有关从 Java 执行此操作的方法,请参见上文)。
Maybe instead of /cygdrive/c/cygwin/bin
, the right directory name would be /usr/bin
.
也许不是/cygdrive/c/cygwin/bin
,正确的目录名称是/usr/bin
.
(Everything is a bit complicated here by having to bridge between Unix and Windows conventions everywhere.)
(这里的一切都有些复杂,因为必须在各处的 Unix 和 Windows 约定之间架起桥梁。)
The simple ls
command can be called like this:
ls
可以这样调用简单的命令:
Process p = Runtime.getRuntime().exec(new String[]{"C:\cygwin\bin\ls.exe", "-la"});
2. Invoking multiple commands:
2. 调用多个命令:
There are basically two ways of invoking multiple commands in one shell:
在一个 shell 中调用多个命令基本上有两种方法:
- passing them all at once to the shell; or
- passing them interactively to the shell.
- 一次将它们全部传递给外壳;或者
- 以交互方式将它们传递给 shell。
For the first way, simply give multiple commands as argument to the -c
option, separated by ;
or \n
(a newline), like this:
对于第一种方法,只需将多个命令作为-c
选项的参数,用;
或\n
(换行符)分隔,如下所示:
bash -c "cd /bin/ ; ls -la"
or from Java (adapting the example above):
或来自 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"});
Here the shell will parse the command line as, and execute it as a script. If it contains multiple commands, they will all be executed, if the shell does not somehow exit before for some reason (like an exit
command). (I'm not sure if the Windows cmd
does work in a similar way. Please test and report.)
在这里,shell 会将命令行解析为,并将其作为脚本执行。如果它包含多个命令,则它们都将被执行,如果 shell 之前由于某种原因(如exit
命令)没有以某种方式退出。(我不确定 Windowscmd
是否以类似的方式工作。请测试并报告。)
Instead of passing the bash (or cmd or whatever shell you are using) the commands on the command line, you can pass them via the Process' input stream.
无需在命令行上传递 bash(或 cmd 或您使用的任何 shell)命令,您可以通过 Process 的输入流传递它们。
- A shell started in "input mode" (e.g. one which got neither the
-c
option nor a shell script file argument) will read input from the stream, and interpret the first line as a command (or several ones). - Then it will execute this command. The command itself might read more input from the stream, if it wants.
- Then the shell will read the next line, interpret it as a command, and execute.
- (In some cases the shell has to read more than one line, for example for long strings or composed commands like if or loops.)
- This will go on until either the end of the stream (e.g. stream.close() at your side) or executing an explicit
exit
command (or some other reasons to exit).
- 以“输入模式”启动的外壳(例如,既没有
-c
选项也没有外壳脚本文件参数的外壳)将从流中读取输入,并将第一行解释为命令(或多个命令)。 - 然后它会执行这个命令。如果需要,命令本身可能会从流中读取更多输入。
- 然后 shell 将读取下一行,将其解释为命令并执行。
- (在某些情况下,shell 必须读取不止一行,例如对于长字符串或像 if 或 loops 这样的组合命令。)
- 这将一直持续到流结束(例如在您身边的 stream.close() )或执行显式
exit
命令(或退出的某些其他原因)。
Here would be an example for this:
下面是一个例子:
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();
回答by AlexR
You do not need cygwin here. There are several pure Java libraries implementing SSH protocol. Use them. BTW they will solve your second problem. You will open session and execute command withing the same session, so the shell state will be preserved automatically.
这里不需要 cygwin。有几个纯 Java 库实现了 SSH 协议。使用它们。顺便说一句,他们将解决您的第二个问题。您将在同一会话中打开会话并执行命令,因此将自动保留 shell 状态。
One example would be JSch.
一个例子是JSch。