2016-03-15 48 views
2

我想讀一個URL的內容同步在斯威夫特一個簡單的命令行批處理腳本。爲了簡單起見,我使用了cURL - 如果必須的話,我知道我可以使用NSURLSession。我也使用OSX上Swift的開源版本swift build來構建它。捲曲通過NSTask不會終止,如果管道出現

的問題是,在某些URL時,NSTask永遠不會終止,如果stdout已經被重定向到一個管道。

// This will hang, and when terminated with Ctrl-C reports "(23) Failed writing body" 
import Foundation 
let task = NSTask() 
let pipe = NSPipe() 
task.launchPath = "/usr/bin/curl" 
task.arguments = ["http://trove.nla.gov.au/newspaper/page/21704647"] 
task.standardOutput = pipe 
task.launch() 
task.waitUntilExit() 

但是,如果您刪除管道或更改URL,任務將成功。

// This will succeed - no pipe 
import Foundation 
let task = NSTask() 
task.launchPath = "/usr/bin/curl" 
task.arguments = ["http://trove.nla.gov.au/newspaper/page/21704647"] 
task.launch() 
task.waitUntilExit() 

// This will succeed - different URL 
import Foundation 
let task = NSTask() 
let pipe = NSPipe() 
task.launchPath = "/usr/bin/curl" 
task.arguments = ["http://trove.nla.gov.au/newspaper/page/21704646"] 
task.standardOutput = pipe 
task.launch() 
task2.waitUntilExit() 

運行的任何直接使用curl從終端成功的例子,所以有一些事情與NSTask的互動,從特定的URL檢索時(和其他幾個人),並在管道存在,那導致cURL失敗。

+0

愚蠢的問題;但是你確定你想在這裏使用「let」而不是「var」。如果任務產生輸出,它是否被賦值回變量本身? – user3069232

+0

對於管道,你的意思是?輸出被傳遞到可讀取的管道。輸出不會替代NSPipe實例本身。 – tobygriffin

+0

好的,但我在代碼中看不到「var」,只是「let」;沒有東西被傳回給任何東西? – user3069232

回答

3

捲曲和NSPipe緩衝數據。根據你在ctrl-c out(這表明curl無法寫入預期的數據量)時得到的錯誤,你在這些錯誤之間會產生不良的交互作用。

嘗試添加-N選項捲曲,以防止它緩衝輸出。

curl也可以輸出進度。我不認爲這是造成問題的原因,但是您可能會添加-s以僅僅保存數據以防萬一。

+0

請注意,作爲賞金的貢獻者,我對解釋/解決方案感興趣,重點討論如何規範管道干擾完成任務的一般問題。根據我的評論,我遇到了swiftc編譯器和stderr的問題,而不是cURL和stdout。 –

+0

@PeterAlfvin你可以發佈代碼示例嗎?我無法使用stderr重現此問題。 – Hod

+0

查看詳情發表於http://stackoverflow.com/questions/41537800/random-hang-by-macos-task-process-when-specifying-stderr-pipe –

5

在@Hod的答案上展開一點:已啓動的 進程的標準輸出被重定向到一個管道,但是您的程序從不讀取其他管道端的 。的管具有有限緩衝器,參見例如 How big is the pipe buffer? ,其解釋了在MacOS管緩衝器大小是(最多)64KB。

如果管道緩衝區已滿,則啓動的過程不能在上面寫 了。如果進程使用阻塞I/O,那麼對管道的一個write()將會阻塞,直到至少可以寫入一個字節。在你的情況下, 不會發生,所以這個過程掛起並且不終止。

只有寫入標準輸出 的數量超過管道緩衝區大小時纔會發生此問題,這就解釋了爲什麼它只發生在某些URL而不發生在其他地方。

作爲溶液,可以從管道,例如讀與

let data = pipe.fileHandleForReading.readDataToEndOfFile() 

之前等待的過程終止。另一種選擇是 使用異步讀取,例如,與來自Real time NSTask output to NSTextView with Swift的代碼:

pipe.fileHandleForReading.readabilityHandler = { fh in 
    let data = fh.availableData 
    // process data ... 
} 

這也將允許從一個進程通過管道不阻塞讀取標準輸出和標準誤差 。