简体   繁体   English

为什么Java CRLF令牌不能用于批处理文件输入?

[英]Why does the Java CRLF token does not work with batch file inputs?

background: 背景:
I once answered this question that was about flushing two input strings from a Java process to a batch script. 我曾经回答过这个问题 ,即将两个输入字符串从Java进程刷新到批处理脚本。 Since I found a workaround solution I am still very interested to solve the remaining mystery and find out why the obvious solution is not working. 由于我找到了一个解决方案解决方案,我仍然非常有兴趣解决剩下的谜团,并找出明显解决方案无效的原因。

problem description 问题描述
See this very simple batch script: 看到这个非常简单的批处理脚本

@ECHO OFF
SET /P input1=1st Input: 
SET /P input2=2nd Input: 
ECHO 1st Input: %input1% and 2nd Input: %input2%

If you run this batch script with Java using ProcessBuilder and flush two input strings into it you will notice that only the first input string will be consumed while the second will be ignored. 如果使用ProcessBuilder使用Java运行此批处理脚本并将两个输入字符串刷入其中,您将注意到只会消耗第一个输入字符串,而忽略第二个输入字符串。 I found out that SET /P command consumes input from pipes when 我发现SET /P命令消耗来自管道的输入

  • CRLF token is found 找到CRLF令牌
  • by timeout 超时
  • by full buffer(1024 Bytes) 通过完全缓冲(1024字节)

My accepted workaround was based on the last two options by using a Thread.sleep(100) statement between the inputs or using a 1024 Byte Buffer for each input. 我接受的解决方法基于最后两个选项,在输入之间使用Thread.sleep(100)语句或对每个输入使用1024字节缓冲区。

It always works for single input or in this case the first input because closing the stream has the effect that the batch script reads one input and empty returns all following SET /P statements. 它总是适用于单个输入,或者在这种情况下是第一个输入,因为关闭流具有批处理脚本读取一个输入并且空返回所有后续SET /P语句的效果。

the question 这个问题
Why is the first option by using the CRLF token "input\\r\\n" not working? 为什么使用CRLF令牌"input\\r\\n"的第一个选项不起作用?

research 研究
I already tried to workaround the String.getBytes() method by creating a Byte Buffer myself using \\x0d and \\x0a as last bytes for CRLF token but it has no effect. 我已经尝试通过使用\\x0d\\x0a作为CRLF令牌的最后字节创建一个字节缓冲区来解决String.getBytes()方法,但它没有任何效果。

And I tried all other OutputStream wrappers like PrintWriter to check if there is a problem with the flush() implementation without any success. 我尝试了所有其他OutputStream包装器,如PrintWriter以检查flush()实现是否存在问题,但没有成功。

I created a C++ program that basically does the same as the java programm by using CreateProcess and stangely it works like a charm. 我创建了一个C ++程序,它通过使用CreateProcess基本上与java程序相同,并且stangely它就像一个魅力。

testing code 测试代码
Not working Java code: 不工作的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();

Full working C++ code: 完整的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);

the question again 这个问题又来了
The problem does not make any sense to me and is bugging me a lot. 这个问题对我没有任何意义,并且让我烦恼不已。 Why does sending the CRLF token from Java does not have any effect on batch file inputs while it does when sending from C++ program? 为什么从Java发送CRLF令牌对批处理文件输入没有任何影响,而从C ++程序发送时呢?

About "put /p" and the pipe and child processes on Windows OS 关于Windows上的“put / p”以及管道和子进程

Just for a test I've a little expanded your test batch to get four input instead of two Now have a look at this nice test 只是为了测试我有点扩展你的测试批次以获得四个输入而不是两个现在看看这个不错的测试

>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
"--"

The interesting thing in here is that the first example works exactly as the java code (only the first "set /P" receive a value, while the second one works as expected More interesting in if you put a line somewhere in the batch file like this one: wmic Process >> TestProcesses.txt by inspecting the TestProcesses.txt, in my enviromnet, I can see that whith the first method (pipe) is present cmd.exe C:\\Windows\\system32\\cmd.exe /S /D /c" test.bat" that isn't present when we use the second one (redirection) 这里有趣的是,第一个示例与java代码完全相同(只有第一个“set / P”接收值,而第二个示例按预期工作)如果您在批处理文件中的某处放置一行,那么更有趣这一个: wmic Process >> TestProcesses.txt通过检查TestProcesses.txt,在我的enviromnet中,我可以看到第一个方法(管道)存在的情况cmd.exe C:\\Windows\\system32\\cmd.exe /S /D /c" test.bat"当我们使用第二个(重定向)时不存在的cmd.exe C:\\Windows\\system32\\cmd.exe /S /D /c" test.bat"

I we run out new test batch (including wmic diagnostics) from java; 我从java中运行新的测试批次(包括wmic诊断); when we inspect the TestProcesses we should see two different processes: 当我们检查TestProcesses时,我们应该看到两个不同的过程:

java.exe              java  -cp .\build\classes javaappcrlf.JavaAppCRLF
cmd.exe               C:\Windows\system32\cmd.exe /c C:\projects\JavaAppCRLF\test.bat  

as in the first method (pipe) we have a separate process for the batch where "put /p" doesnt works 与第一种方法(管道)一样,我们对批处理有一个单独的处理,其中“put / p”不起作用

From the chapter Pipes and CMD.exe of the article Pipes and CMD.exe 从文章的篇章管道和的CMD.exe 管道和的CMD.exe

This has several side effects: Any newline (CR/LF) characters in the batch_command will be turned into & operators. 这有几个副作用:batch_command中的任何换行符(CR / LF)都将变为&运算符。 see StackOverflow If the batch_command includes any caret escape characters ^ they will need to be doubled up so that the escape survives into the new CMD shell. 请参阅StackOverflow如果batch_command包含任何插入符转义字符^,则需要将它们加倍,以便转义能够存活到新的CMD shell中。

also the linked article on stack overflow is interesting 关于堆栈溢出的链接文章也很有趣

About C++ Testing 关于C ++测试

I make a little change to the c++ program described in Creating a Child Process with Redirected Input and Output just to read a file of four lines ad passing its content to a child process that execute our batch through a pipe and the results are the same of your Java program 我对使用重定向输入和输出创建子进程中描述的c ++程序稍作修改,只是为了读取四行文件,将其内容传递给通过pipe执行批处理的子进程,结果与你的Java程序

Alternative refactoring/workaround 替代重构/解决方法

from the findings mentioned above, it comes that a java program that read and write to (temporary) files ( ...I know is not the same thing ) should works; 从上面提到的调查结果来看,一个读取和写入(临时)文件的java程序( ......我知道不是同一个东西 )应该可行; I successfully tested a working solution by changing the builder this way 我通过这种方式更改构建器,成功测试了一个可行的解决方

    ProcessBuilder builder = new ProcessBuilder(
            "cmd",
            "/c",
            "(C:\\projects\\JavaAppCRLF\\test4.bat < C:\\projects\\JavaAppCRLF\\tmp-test4.in)",
            ">",
            "C:\\projects\\JavaAppCRLF\\tmp-test4.out"
    );

Post Scriptum: an interesting note about other shell (ie: bash on ”os x" or linux ) Post Scriptum:关于其他shell的一个有趣的注释(即:bash on“os x”或linux)

AFAIK not all the other platforms suffers this "issue" in the same way; AFAIK并非所有其他平台都以同样的方式遭受这个“问题”; ie on a bash (os x terminal) I made the following test with a script that acts just as our previous testing under Windows: 即在bash(os x终端)上,我使用一个脚本进行了以下测试,该脚本与我们之前在Windows下的测试一样:

cd ~/projects/so-test/java-crlf-token/JavaAppCRLF  
$ cat test.sh
#!/bin/bash - 
# SET /P input1=1st Input: 
echo -n "1st Input:"; 
read input1;
#SET /P input2=2nd Input: 
echo -n "2nd Input:"; 
read input2;
#ECHO 1st Input: %input1% and 2nd Input: %input2%
echo -n "1st Input: ${input1} and 2nd Input: ${input2}"

then the only one changed to the java program is to reference the script: 然后唯一一个改为java程序的是引用脚本:

ProcessBuilder builder = new ProcessBuilder("/Users/userx/projects/so-test/java-crlf-token/JavaAppCRLF/test.sh");

let's see what happens: 让我们看看发生了什么:

$ 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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM