为什么Java CRLF令牌不能与批处理文件输入一起工作?
背景:为什么Java CRLF令牌不能与批处理文件输入一起工作?,java,windows,batch-file,process,pipe,Java,Windows,Batch File,Process,Pipe,背景: 我曾经回答过 这是关于将两个输入字符串从Java进程刷新到批处理脚本。自从我找到了一个解决方案,我仍然 非常有兴趣解决剩余的谜团,并找出为什么显而易见的解决方案不起作用 问题描述 请参见以下非常简单的批处理脚本: @ECHO OFF SET /P input1=1st Input: SET /P input2=2nd Input: ECHO 1st Input: %input1% and 2nd Input: %input2% 如果您使用ProcessBuilder使用Java运行
我曾经回答过 这是关于将两个输入字符串从Java进程刷新到批处理脚本。自从我找到了一个解决方案,我仍然 非常有兴趣解决剩余的谜团,并找出为什么显而易见的解决方案不起作用 问题描述
请参见以下非常简单的批处理脚本:
@ECHO OFF
SET /P input1=1st Input:
SET /P input2=2nd Input:
ECHO 1st Input: %input1% and 2nd Input: %input2%
如果您使用ProcessBuilder
使用Java运行此批处理脚本,并将两个输入字符串刷新到其中,您将注意到只有
将使用第一个输入字符串,而忽略第二个输入字符串。
我发现SET/P
命令在
- 找到CRLF令牌
- 超时
- 按完全缓冲区(1024字节)
Thread.sleep(100)
语句或使用1024字节
每个输入的缓冲区。
它总是适用于单个输入,或者在本例中是第一个输入,因为关闭流会产生效果 批处理脚本读取一个输入并为空将返回以下所有
SET/P
语句
问题为什么使用CRLF令牌的第一个选项
“input\r\n”
不起作用
研究我已经尝试过通过使用
\x0d
和\x0a
创建字节缓冲区来解决String.getBytes()
方法
CRLF令牌的字节数,但无效
我尝试了所有其他的OutputStream
包装器,比如PrintWriter
,来检查是否有
flush()
实现出现问题,但没有成功
<>我创建了一个C++程序,它基本上和java程序一样,使用<代码> CuraTypActual>代码,它的工作方式就像一个符咒。
测试代码不工作的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();
完全工作的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);
问题又来了这个问题对我来说毫无意义,让我很烦恼。为什么从Java发送CRLF令牌没有任何 从C++程序、关于“PU/P”和Windows O.S上的管道和子进程发送时对批处理文件输入的影响。 只是为了一个测试,我稍微扩展了您的测试批,以获得四个输入,而不是两个 现在看看这个漂亮的测试
>type test.txt | test.bat
1st Input:2nd Input:3rd Input:4th Input:1st Input: one and 2nd Input: and 3rd
Input: and 4rd Input:
"--"
>test.bat < test.txt
1st Input:2nd Input:3rd Input:4th Input:1st Input: one and 2nd Input: two and
3rd Input: three and 4rd Input: four
"--"
与第一种方法(管道)一样,我们对“投入/产出”不起作用的批次有一个单独的过程
从文章的管道和CMD.exe章节
这有几个副作用:文本中的任何换行符(CR/LF)
批处理命令将转换为&运算符。看看是否
batch_命令包含它们将使用的任何插入符号转义字符^
需要加倍,以便逃逸者能在新的CMD中生存
贝壳
关于堆栈溢出的链接文章也很有趣
关于C++测试
我对C++程序做了一些改动。
只需读取一个四行的文件,将其内容传递给一个子进程,该子进程通过管道执行批处理,结果与Java程序相同
替代重构/解决方案
从上面提到的发现来看,一个读写(临时)文件的java程序(我知道不是一回事)应该可以工作;通过以这种方式更改生成器,我成功地测试了一个工作解决方案
ProcessBuilder builder = new ProcessBuilder(
"cmd",
"/c",
"(C:\\projects\\JavaAppCRLF\\test4.bat < C:\\projects\\JavaAppCRLF\\tmp-test4.in)",
">",
"C:\\projects\\JavaAppCRLF\\tmp-test4.out"
);
那么,唯一更改为java程序的是引用脚本:
ProcessBuilder builder = new ProcessBuilder("/Users/userx/projects/so-test/java-crlf-token/JavaAppCRLF/test.sh");
让我们看看会发生什么:
$ cat test.txt
abc
cde
# :pipe
$ cat test.txt | test.sh
$ cat test.txt | ./test.sh
1st Input:2nd Input:1st Input: abc and 2nd Input: cde
# :redirection
$ ./test.sh < test.txt
1st Input:2nd Input:1st Input: abc and 2nd Input: cde
# :java
$ java -cp build/classes/ javaappcrlf.JavaAppCRLF
1st Input:2nd Input:1st Input: foo
and 2nd Input: bar
$cat test.txt
abc
cde
#:管道
$cat test.txt | test.sh
$cat test.txt |./test.sh
第一输入:第二输入:第一输入:abc和第二输入:cde
#:重定向
$./test.sh
我建议您尝试一下这个技巧:发送到批处理文件的每一行的长度必须正好是1023字节,也就是说,您应该发送“foo\r\n”
而不是发送“foo\r\n空间\r\n”
,因此字符数应该是多少。从第一个“f”到最后一个“\n”正好是1023。对第二行执行相同的操作。有关详细信息,请参阅。假设您可以推迟读取process.getInputStream(),直到将所有数据发送到process.getOutputStream(),这是不正确的。这些流同时运行。您应该将读取或写入移动到一个单独的线程(或者调用processBuilder.redirectOutput(processBuilder.Redirect.INHERIT)
)。@Aacini正如我在问题中所写的,我知道这个技巧,并且它是有效的。但我的问题是为什么这个1024字节的解决方案是必要的。@VGR您是对的。为输入和输出管道使用不同的线程是正确的方法。但是当我这样做的时候,我没有得到其他的结果。这与我的问题无关。我甚至可以删除整个过程的输出读取部分,仍然只有一个输入发送到批处理文件。这也是一个只有批处理文件的问题。对于其他流程,这很好。而重定向输出也没有帮助。这很公平。我只是想排除可能的原因。非常感谢你的全面研究。正如@Aacini已经指出的,所有这些不当行为都可能是win cmd的一个bug。将等待一到两天再奖励赏金。@我添加了一条注释,因为我通过更改生成器成功测试了一个变通解决方案(可能对某些人来说很有趣),希望这不被认为超出了问题的范围:-)我对这个答案感到困惑。在我上面的第一个评论中,我给出了一个线程的链接,其中已经发布了关于这种行为的更广泛的测试。我还评论说,出现这个问题是“因为cmd.exe中有一个bug”。然而,OP回答说:“我的问题是:为什么这个1024字节的解决方案是必要的?”。
ProcessBuilder builder = new ProcessBuilder("/Users/userx/projects/so-test/java-crlf-token/JavaAppCRLF/test.sh");
$ cat test.txt
abc
cde
# :pipe
$ cat test.txt | test.sh
$ cat test.txt | ./test.sh
1st Input:2nd Input:1st Input: abc and 2nd Input: cde
# :redirection
$ ./test.sh < test.txt
1st Input:2nd Input:1st Input: abc and 2nd Input: cde
# :java
$ java -cp build/classes/ javaappcrlf.JavaAppCRLF
1st Input:2nd Input:1st Input: foo
and 2nd Input: bar