我在这里结束了我的智慧.我确信这很简单,我很可能在理解java和流时遇到很大漏洞.我认为有这么多课程,我试图通过API来弄清楚我何时以及如何使用大量的输入/输出流,我有点不知所措.
我刚刚了解了apache commons库的存在(自学java失败),我正在尝试将我的一些Runtime.getRuntime().exec转换为使用commons - exec.已经修复了一些每6个月一次这个问题,然后消除了exec的风格问题.
代码执行perl脚本,并在GUI运行时从GUI中显示stdout.
调用代码在swingworker内部.
我迷路了如何使用pumpStreamHandler ...无论如何这里是旧代码:
String pl_cmd = "perl script.pl"
Process p_pl = Runtime.getRuntime().exec( pl_cmd );
BufferedReader br_pl = new BufferedReader( new InputStreamReader( p_pl.getInputStream() ) );
stdout = br_pl.readLine();
while ( stdout != null )
{
output.displayln( stdout );
stdout = br_pl.readLine();
}
Run Code Online (Sandbox Code Playgroud)
我想这是我得到的复制粘贴代码,我很久以前还不完全理解.上面我假设正在执行该过程,然后抓取输出流(通过"getInputStream"?),将其放入缓冲读取器,然后将循环,直到缓冲区为空.
我没有得到的是为什么这里不需要'waitfor'样式命令?是否可能有一段时间缓冲区将为空,退出循环,并在进程仍在进行时继续?当我运行它时,情况似乎并非如此.
无论如何,我试图使用commons exec获得相同的行为,基本上再次从谷歌找到的代码:
DefaultExecuteResultHandler rh = new DefaultExecuteResultHandler();
ExecuteWatchdog wd = new ExecuteWatchdog( ExecuteWatchdog.INFINITE_TIMEOUT );
Executor exec = new DefaultExecutor();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler psh = new PumpStreamHandler( out ); …Run Code Online (Sandbox Code Playgroud) 我试图决定是否使用ProcessBuilder或Commons exec,
我的要求是我只是想创建一个守护进程,其stdout/stdin/stderr我并不关心.另外,我希望在时机成熟时执行kill来销毁这个进程.
我在Linux上使用Java.
我知道两者都有他们的痛苦和陷阱(例如确保使用单独的线程吞下流可能导致阻塞或死锁,并关闭流,以免留下打开的文件)并想知道是否有人有建议方式或其他方式以及任何良好的资源.
我需要将一个文本参数传递给使用Apache Commons Exec启动的命令的stdin(对于好奇,命令是gpg,参数是密钥库的密码; gpg没有明确提供密码的参数,仅从stdin接受它.
另外,我需要它来支持Linux和Windows.
在shell脚本中,我会这样做
cat mypassphrase|gpg --passphrase-fd
Run Code Online (Sandbox Code Playgroud)
要么
type mypassphrase|gpg --passphrase-fd
Run Code Online (Sandbox Code Playgroud)
但是类型在Windows上不起作用,因为它不是可执行文件,而是命令内置的命令(cmd.exe).
该代码不工作(由于上述原因)低于.为此产生一个完整的外壳太难看了,我一直在寻找更优雅的解决方案.不幸的是,BouncyCastle库和PGP之间存在一些不兼容的问题,因此我无法在(非常短的)时间内使用完全编程的解决方案.
提前致谢.
CommandLine cmdLine = new CommandLine("type");
cmdLine.addArgument(passphrase);
cmdLine.addArgument("|");
cmdLine.addArgument("gpg");
cmdLine.addArgument("--passphrase-fd");
cmdLine.addArgument("0");
cmdLine.addArgument("--no-default-keyring");
cmdLine.addArgument("--keyring");
cmdLine.addArgument("${publicRingPath}");
cmdLine.addArgument("--secret-keyring");
cmdLine.addArgument("${secretRingPath}");
cmdLine.addArgument("--sign");
cmdLine.addArgument("--encrypt");
cmdLine.addArgument("-r");
cmdLine.addArgument("recipientName");
cmdLine.setSubstitutionMap(map);
DefaultExecutor executor = new DefaultExecutor();
int exitValue = executor.execute(cmdLine);
Run Code Online (Sandbox Code Playgroud) 要么Apache Commons Exec中存在错误,要么我错误地使用API,但是当我使用CommandLine类添加包含空格的参数时,会添加一些引号,然后是给定参数的一部分.
例如:当我打电话java "what version",我得到java.lang.NoClassDefFoundError: what version了,当我打电话java "\"what version\""(其中包含转义引号,是命令行参数本身的一部分),我得到java.lang.NoClassDefFoundError: "what version".
因此,以下测试失败,因为正如您在最后一行中所看到的,Apache Exec正在生成后者版本,它应该生成第一个版本:
@Test
public void testArgumentQuoting() throws Exception {
DefaultExecutor executor = new DefaultExecutor();
DefaultExecuteResultHandler resultHandler = new DefaultExecuteResultHandler();
ByteArrayOutputStream out = new ByteArrayOutputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(out, out);
executor.setStreamHandler(streamHandler);
CommandLine cmdLine = new CommandLine("java");
cmdLine.addArgument("what version");
executor.execute(cmdLine, resultHandler);
resultHandler.waitFor();
String resultPattern = "Exception in thread \"main\" java\\.lang\\.NoClassDefFoundError: ([\\w \"]+)";
Pattern pattern = Pattern.compile(resultPattern);
Matcher matcher = …Run Code Online (Sandbox Code Playgroud) 警告 - 原因不是缺少文件 - 所有线程都在呼叫相同的脚本文件
我开始5-6个线程,在Red Hat框中调用本地脚本.
我注意到有时,我收到以下错误消息
couldn't read file "/home/leo/myScript.exp": no such file or directory
Run Code Online (Sandbox Code Playgroud)
显然,所有进程都在执行脚本,因此它似乎与[1]操作系统有关,对可以运行脚本或访问文件进行读取的同时进程有一些限制,或者[2] Java正在尝试执行某些操作一些未准备好的流(我假设commons-exec会为我处理这个)
这是代码
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
CommandLine commandline = CommandLine.parse("/home/leo/myScript.exp");
DefaultExecutor exec = new DefaultExecutor();
PumpStreamHandler streamHandler = new PumpStreamHandler(outputStream);
exec.setStreamHandler(streamHandler);
try {
exec.execute(commandline); <<< error happens here
}catch(IOException io) {
throw new Exception("");
}
Run Code Online (Sandbox Code Playgroud)
如果错误是[1],那么我想知道如何在linux OS中放宽这个限制
如果错误是[2],那么我想知道如何告诉commons-exec等待资源准备好(在最坏的情况下我只是添加一些重试,但我认为这不是很优雅)
如果错误是别的,至少知道原因将足以让我找到一些解决方案.
更新 - 3月15日
嗯,这就是事情.
该脚本是一个期望脚本,它使用库来调用Java类.
我注意到的一件事是脚本运行良好,直到它调用一个创建数据库连接的java方法.
因为线程数量很少(3~5)我不认为这是数据库中的问题.相反,在我看来,在调用java代码时和/或在java代码创建数据库连接时阻止调用脚本.
我仍然试图得到确切的异常,但是期望脚本看起来像这样(有点)
#!/opt/tclblend/bin/expect -f
set edfDir "/usr/local/nssa/bin/edf";
set env(LD_LIBRARY_PATH) "/opt/tclblend/lib/tcljava1.4.1"; # for tclBlend
## always use …Run Code Online (Sandbox Code Playgroud) 我一直在寻找一个很好的例子,用于将Process输出和错误流写入日志文件.我使用apache-commons exec库来执行我的进程.下面的代码示例来演示
public static int executeCommand(CommandLine command, Logger log) throws ExecuteException, IOException {
DefaultExecutor executor = new DefaultExecutor();
executor.setExitValue(0);
PumpStreamHandler psh = new PumpStreamHandler();
executor.setStreamHandler(psh);
return executor.execute(command);
}
Run Code Online (Sandbox Code Playgroud) 我正在编写一个Java应用程序,需要使用Apache Commons Exec库来使用外部命令行应用程序.我需要运行的应用程序具有相当长的加载时间,因此最好保持一个实例处于活动状态而不是每次都创建一个新进程.应用程序的工作方式非常简单.一旦启动,它会等待一些新输入并生成一些数据作为输出,这两个数据都使用应用程序的标准I/O.
因此,我们的想法是执行CommandLine,然后将PumpStreamHandler与三个独立的流(输出,错误和输入)一起使用,并使用这些流与应用程序进行交互.到目前为止,我已经在基本场景中完成了这项工作,我有一个输入,一个输出,然后应用程序关闭.但是一旦我试图进行第二次交易,就会出现问题.
在创建了我的CommandLine之后,我创建了我的Executor并像这样启动它:
this.executor = new DefaultExecutor();
PipedOutputStream stdout = new PipedOutputStream();
PipedOutputStream stderr = new PipedOutputStream();
PipedInputStream stdin = new PipedInputStream();
PumpStreamHandler streamHandler = new PumpStreamHandler(stdout, stderr, stdin);
this.executor.setStreamHandler(streamHandler);
this.processOutput = new BufferedInputStream(new PipedInputStream(stdout));
this.processError = new BufferedInputStream(new PipedInputStream(stderr));
this.processInput = new BufferedOutputStream(new PipedOutputStream(stdin));
this.resultHandler = new DefaultExecuteResultHandler();
this.executor.execute(cmdLine, resultHandler);
Run Code Online (Sandbox Code Playgroud)
然后我继续启动三个不同的线程,每个线程处理一个不同的流.我还有三个处理输入和输出的SynchronousQueues(一个用作输入流的输入,一个用于通知outputQueue已启动新命令而另一个用于输出).例如,输入流线程如下所示:
while (!killThreads) {
String input = inputQueue.take();
processInput.write(input.getBytes());
processInput.flush();
IOQueue.put(input);
}
Run Code Online (Sandbox Code Playgroud)
如果我删除while循环并执行一次,一切似乎都完美无缺.显然,如果我再次尝试执行它,PumpStreamHandler会抛出异常,因为它已被两个不同的线程访问.
这里的问题是,在线程结束之前,似乎没有真正刷新processInput.调试时,命令行应用程序仅在线程结束时才真正接收其输入,但如果保留while循环则永远不会获得它.我已经尝试了许多不同的东西来使processInput刷新,但似乎没有任何工作.
以前有人尝试过类似的东西吗?有什么我想念的吗?任何帮助将不胜感激!
java command-line multithreading inputstream apache-commons-exec
我在我的Java程序中启动了一个外部进程(在Linux上),我需要能够发送一个SIGTERM信号,而不是exec.getWatchdog().destroyProcess()发送的SIGKILL .有没有办法可以更优雅地停止使用commons-exec启动的unix进程?或者我可以获得PID,以便我可以自己运行相应的kill命令吗?
我正在尝试执行一个程序(从ImageMagick转换为具体),其父文件夹存在于路径上.因此,当我从命令行运行时convert,它会运行命令.但是,以下内容失败:
String command = "convert"
CommandLine commandLine = CommandLine.parse(command);
commandLine.addArgument(...)
...
int exitValue = executor.execute(commandLine);
Run Code Online (Sandbox Code Playgroud)
如果我指定convert executable(C:\Program files\...)的完整路径,则此代码有效.如果我不这样做,我会抛出一个带退出值的异常4.
如何让commons-exec识别系统路径?
java path environment-variables system-calls apache-commons-exec
我想使用org.apache.commons.execJava 库来调用可执行文件。该CommandLine对象是否可以防止命令行注入?例如,如果我打电话:
String singleStringArgument = "-whatever;rm -rf ~/*"; // evil looking argument!
CommandLine cl = new CommandLine(new File(pathToExe,exeName));
cl.addArgument(singleStringArgument); // oh no!
Executor exe = new DefaultExecutor();
exe.execute(cl);
Run Code Online (Sandbox Code Playgroud)
rm -rf ~/*除了预期的命令之外还会运行吗?如果确实如此,防止这种情况的最佳方法是什么?
API 说addArgument()“处理引用”,但我不确定这在这种情况下意味着什么。我可以编写一个测试用例来看看我的 Linux 机器上会发生什么,但我想确保它在其他平台上也是安全的。
java ×10
command-line ×2
exec ×1
inputstream ×1
linux ×1
path ×1
process ×1
runtime ×1
system-calls ×1
unix ×1