Arc*_*ord 5 java windows pipe process batch-file
背景:
我曾经回答过这个问题
,即将两个输入字符串从Java进程刷新到批处理脚本.由于我找到了一个解决方案解决方案,我仍然非常有兴趣解决剩下的谜团,并找出明显解决方案无效的原因.
问题描述
看到这个非常简单的批处理脚本
@ECHO OFF
SET /P input1=1st Input:
SET /P input2=2nd Input:
ECHO 1st Input: %input1% and 2nd Input: %input2%
Run Code Online (Sandbox Code Playgroud)
如果您使用Java运行此批处理脚本ProcessBuilder
并将两个输入字符串刷入其中,您会注意到只会消耗第一个输入字符串,而忽略第二个输入字符串.我发现该SET /P
命令消耗了来自管道的输入
我接受的解决方法基于最后两个选项,Thread.sleep(100)
在输入之间使用语句或对每个输入使用1024字节缓冲区.
它始终适用于单个输入,或者在这种情况下是第一个输入,因为关闭流具有批处理脚本读取一个输入并且空返回所有后续SET /P
语句的效果.
问题
为什么使用CRLF令牌的第一个选项"input\r\n"
不起作用?
研究
我已经尝试String.getBytes()
通过自己创建一个字节缓冲区\x0d
并使用\x0a
CRLF令牌的最后字节来解决该方法,但它没有任何效果.
我尝试了所有其他OutputStream
包装器,PrintWriter
以检查实现是否存在问题flush()
而没有任何成功.
我创建了一个C++程序,它基本上与java程序一样,使用CreateProcess
和stangely它就像一个魅力.
测试代码
不工作的Java代码:
ProcessBuilder builder = new ProcessBuilder("test.bat");
Process process = builder.start();
OutputStream out = process.getOutputStream();
out.write("foo\r\n".getBytes());
out.flush();
out.write("bar\r\n".getBytes());
out.flush();
out.close();
BufferedReader in = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = in.readLine()) != null)
System.out.println(line);
in.close();
Run Code Online (Sandbox Code Playgroud)
完整的C++代码:
DWORD dwWritten;
char cmdline[] = "test.bat";
CHAR Input1[] = "foo\r\n";
CHAR Input2[] = "bar\r\n";
HANDLE hStdInRd = NULL;
HANDLE hStdInWr = NULL;
SECURITY_ATTRIBUTES saAttr;
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
// Create Pipe
saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;
CreatePipe(&hStdInRd, &hStdInWr, &saAttr, 0);
SetHandleInformation(hStdInWr, HANDLE_FLAG_INHERIT, 0);
// Create Process
ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION));
ZeroMemory( &siStartInfo, sizeof(STARTUPINFO));
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
siStartInfo.hStdInput = hStdInRd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, cmdline, NULL, NULL, TRUE, 0, NULL, NULL, &siStartInfo, &piProcInfo);
CloseHandle(piProcInfo.hProcess);
CloseHandle(piProcInfo.hThread);
// Write to Pipe
WriteFile(hStdInWr, Input1, (DWORD)strlen(Input1), &dwWritten, NULL);
FlushFileBuffers(hStdInWr);
WriteFile(hStdInWr, Input2, (DWORD)strlen(Input2), &dwWritten, NULL);
FlushFileBuffers(hStdInWr);
CloseHandle(hStdInWr);
Run Code Online (Sandbox Code Playgroud)
这个问题
对我来说没有任何意义,并且让我烦恼不已.为什么从Java发送CRLF令牌对批处理文件输入没有任何影响,而从C++程序发送时呢?
只是为了进行测试,我稍微扩展了您的测试批次以获得四个输入而不是两个\n现在看看这个不错的测试
\n\n>type test.txt | test.bat\n1st Input:2nd Input:3rd Input:4th Input:1st Input: one and 2nd Input: and 3rd\nInput: and 4rd Input:\n"--"\n\n>test.bat < test.txt\n1st Input:2nd Input:3rd Input:4th Input:1st Input: one and 2nd Input: two and\n3rd Input: three and 4rd Input: four\n"--"\n
Run Code Online (Sandbox Code Playgroud)\n\n这里有趣的是,第一个示例的工作方式与 java 代码完全相同(只有第一个“set /P”接收值,而第二个示例按预期工作\n更有趣的是,如果您在批处理文件中的某处放置一行像这样:wmic Process >> TestProcesses.txt
\n通过检查 TestProcesses.txt,在我的环境中,我可以看到第一个方法(管道)存在cmd.exe C:\\Windows\\system32\\cmd.exe /S /D /c" test.bat"
,但当我们使用第二个方法(重定向)时不存在
我们从 java 运行了新的测试批次(包括 wmic 诊断);当我们检查 TestProcesses 时,我们应该看到两个不同的进程:
\n\njava.exe java -cp .\\build\\classes javaappcrlf.JavaAppCRLF\ncmd.exe C:\\Windows\\system32\\cmd.exe /c C:\\projects\\JavaAppCRLF\\test.bat \n
Run Code Online (Sandbox Code Playgroud)\n\n与第一种方法(管道)一样,我们有一个单独的批处理过程,其中“put /p”不起作用
\n\n来自文章管道和 CMD.exe的管道和 CMD.exe一章
\n\n\n\n\n这有几个副作用:\nbatch_command 中的任何换行符 (CR/LF) 都将转换为 & 运算符。请参阅StackOverflow 如果\n batch_command 包含任何脱字符转义字符 ^ 它们将\n 需要加倍,以便转义能够在新的 CMD\n shell 中保留。
\n
关于堆栈溢出的链接文章也很有趣
\n\n我对 \n创建具有重定向输入和输出的子进程中描述的 C++ 程序做了一些更改只是为了读取一个四行文件并将其内容传递给通过 a 执行批处理的子进程pipe
,结果是相同的你的Java程序的
从上面提到的发现来看,读取和写入(临时)文件的 java 程序(...我知道不是同一件事)应该可以工作;我通过这种方式更改构建器成功测试了工作解决方案
\n\n ProcessBuilder builder = new ProcessBuilder(\n "cmd",\n "/c",\n "(C:\\\\projects\\\\JavaAppCRLF\\\\test4.bat < C:\\\\projects\\\\JavaAppCRLF\\\\tmp-test4.in)",\n ">",\n "C:\\\\projects\\\\JavaAppCRLF\\\\tmp-test4.out"\n );\n
Run Code Online (Sandbox Code Playgroud)\n\nAFAIK 并非所有其他平台都以同样的方式遭受这个“问题”;即\非 bash(os x 终端)我使用脚本进行了以下测试,该脚本的作用与我们之前在 Windows 下的测试相同:
\n\ncd ~/projects/so-test/java-crlf-token/JavaAppCRLF \n$ cat test.sh\n#!/bin/bash - \n# SET /P input1=1st Input: \necho -n "1st Input:"; \nread input1;\n#SET /P input2=2nd Input: \necho -n "2nd Input:"; \nread input2;\n#ECHO 1st Input: %input1% and 2nd Input: %input2%\necho -n "1st Input: ${input1} and 2nd Input: ${input2}"\n
Run Code Online (Sandbox Code Playgroud)\n\n那么对java程序唯一的改变就是引用脚本:
\n\nProcessBuilder builder = new ProcessBuilder("/Users/userx/projects/so-test/java-crlf-token/JavaAppCRLF/test.sh");\n
Run Code Online (Sandbox Code Playgroud)\n\n让我们看看发生了什么:
\n\n$ cat test.txt\nabc\ncde\n\n# :pipe\n$ cat test.txt | test.sh\n$ cat test.txt | ./test.sh\n1st Input:2nd Input:1st Input: abc and 2nd Input: cde \n\n# :redirection\n$ ./test.sh < test.txt\n1st Input:2nd Input:1st Input: abc and 2nd Input: cde\n\n# :java \n$ java -cp build/classes/ javaappcrlf.JavaAppCRLF\n1st Input:2nd Input:1st Input: foo\nand 2nd Input: bar\n
Run Code Online (Sandbox Code Playgroud)\n
归档时间: |
|
查看次数: |
292 次 |
最近记录: |