Why doesn't Java CRLF token work with batch file input?

background:
I somehow answered this question which was to flush the two input lines from the Java process into a batch script. Since I found a workaround solution, I am still very interested in resolving the remaining mystery and figuring out why the obvious solution does not work.

description of the problem
Check out 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 of script using Java using ProcessBuilder and run two input lines into it, you will notice that only the first input line will be consumed and the second will be ignored. I found out that the SET /P command consumes input from pipes when

  • CRLF Icon Found
  • using timeout
  • full buffer (1024 bytes)

My workaround was based on the last two parameters, using the Thread.sleep(100) instruction between inputs or using 1024 bytes of Buffer for each input.

It always works for a single input, or in this case the first input, because closing the stream has the effect that the script package reads one input and returns all the following SET /P statements empty.

question
Why does the first option using the CRLF token "input\r\n" not work?

research
I already tried using the String.getBytes() method, creating the byte buffer itself, using \x0d and \x0a as the last byte for the CRLF token, but it has no effect.

And I tried all other OutputStream wrappers like PrintWriter to check if there is a problem with flush() implementation without any success.

I created a C ++ program that basically does the same thing as a java program using CreateProcess and, oddly enough, works like a charm.

test code
Java code does not work:

 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:

 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); 

question again
The problem doesn't make any sense to me, and I'm joking a lot. Why does sending a CRLF token with Java have no effect on the inputs of a batch file when it is executed when sending from a C ++ program?

+5
source share
1 answer

About put / p and the channel and child processes on Windows OS

Just for the test, I expanded your test batch a bit to get four inputs instead of two. Now let's look at this good 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 "--" 

Interestingly, the first example works just like Java code (only the first "set / P" gets the value, and the second works as expected. More interesting if you put the line somewhere in a batch file like this: wmic Process >> TestProcesses.txt by checking TestProcesses.txt in my enviromnet, I see that the first method (pipe) is cmd.exe C:\Windows\system32\cmd.exe /S /D /c" test.bat" , which is not when we use the second (redirect)

I am starting a new test batch (including wmic diagnostics) from java; when we test the test processes, we should see two different processes:

 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" does not work

In the Pipes and CMD.exe chapter, Pipes and CMD.exe articles

This has several side effects: Any newline characters (CR / LF) in batch_command will also include statements. see fooobar.com/questions/6270 / ... If the batch_command command contains any carriage escape characters; they will need to be doubled so that the escape survives in the new CMD shell.

a related article on stack overflow is also interesting

About C ++ Testing

I am slightly modifying the C ++ program described in Creating a child process with redirected inputs and outputs to read a four-line declaration file that passes its contents to a child process that runs our batch through a pipe and the results match your Java program

Alternative refactoring / workaround

from the above conclusions, it comes that a Java program that reads and writes (temporary) files (... I know this is not the same thing) should work; I successfully tested the working solution by changing the constructor 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: interesting note about another shell (ie: bash on "os x" or linux)

AFAIK not all other platforms also suffer from this "problem"; that is, on bash (os x terminal), I did the following test with a script that acts the same as the previous test under 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 thing that was changed in the java program is the link to the script:

 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 
+3
source

Source: https://habr.com/ru/post/1259037/


All Articles