Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/326.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么Java CRLF令牌不能与批处理文件输入一起工作?_Java_Windows_Batch File_Process_Pipe - Fatal编程技术网

为什么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