簡體   English   中英

為什么Java CRLF令牌不能用於批處理文件輸入?

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

背景:
我曾經回答過這個問題 ,即將兩個輸入字符串從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作為CRLF令牌的最后字節創建一個字節緩沖區來解決String.getBytes()方法,但它沒有任何效果。

我嘗試了所有其他OutputStream包裝器,如PrintWriter以檢查flush()實現是否存在問題,但沒有成功。

我創建了一個C ++程序,它通過使用CreateProcess基本上與java程序相同,並且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();

完整的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 ++程序發送時呢?

關於Windows上的“put / p”以及管道和子進程

只是為了測試我有點擴展你的測試批次以獲得四個輸入而不是兩個現在看看這個不錯的測試

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

這里有趣的是,第一個示例與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"

我從java中運行新的測試批次(包括wmic診斷); 當我們檢查TestProcesses時,我們應該看到兩個不同的過程:

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

與第一種方法(管道)一樣,我們對批處理有一個單獨的處理,其中“put / p”不起作用

從文章的篇章管道和的CMD.exe 管道和的CMD.exe

這有幾個副作用:batch_command中的任何換行符(CR / LF)都將變為&運算符。 請參閱StackOverflow如果batch_command包含任何插入符轉義字符^,則需要將它們加倍,以便轉義能夠存活到新的CMD shell中。

關於堆棧溢出的鏈接文章也很有趣

關於C ++測試

我對使用重定向輸入和輸出創建子進程中描述的c ++程序稍作修改,只是為了讀取四行文件,將其內容傳遞給通過pipe執行批處理的子進程,結果與你的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"
    );

Post Scriptum:關於其他shell的一個有趣的注釋(即:bash on“os x”或linux)

AFAIK並非所有其他平台都以同樣的方式遭受這個“問題”; 即在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}"

然后唯一一個改為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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM