2
我有一個外部控制檯應用程序(在OS X上),它將從1到100的整數序列發送到標準輸出,大約每秒一次。Swift:如何在不等待進程完成的情況下讀取子進程中的標準輸出
我斯威夫特,我需要使用該數字流來更新進度指示器。
下面是代碼,我到目前爲止有:
class MasterViewController: NSViewController {
@IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "sleep 1; echo 10 ; sleep 1 ; echo 20 ; sleep 1 ; echo 30 ; sleep 1 ; echo 40; sleep 1; echo 50; sleep 1; echo 60; sleep 1"]
let pipe = Pipe()
task.standardOutput = pipe
task.launch()
let data = pipe.fileHandleForReading.readDataToEndOfFile()
if let string = String(data: data, encoding: String.Encoding.utf8) {
print(string)
}
}
代碼工作 - 也就是說,它讀取命令行實用程序的輸出,並修改進度指示器相應地,但它使所有的在之後更改實用程序退出(並使得我的用戶界面在此期間等待)。
我該如何設置它,以便它從後臺應用程序讀取輸出並實時更新進度指示器?
UPDATE
以供將來參考,這是我如何得到它到底(現在更新的斯威夫特3)工作:現在
class ViewController: NSViewController {
@IBOutlet weak var progressIndicator: NSProgressIndicator!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
let task = Process()
task.launchPath = "/bin/sh"
task.arguments = ["-c", "sleep 1; echo 10 ; sleep 1 ; echo 20 ; sleep 1 ; echo 30 ; sleep 1 ; echo 40; sleep 1; echo 50; sleep 1; echo 60; sleep 1"]
let pipe = Pipe()
task.standardOutput = pipe
let outHandle = pipe.fileHandleForReading
outHandle.waitForDataInBackgroundAndNotify()
var progressObserver : NSObjectProtocol!
progressObserver = NotificationCenter.default.addObserver(
forName: NSNotification.Name.NSFileHandleDataAvailable,
object: outHandle, queue: nil)
{
notification -> Void in
let data = outHandle.availableData
if data.count > 0 {
if let str = String(data: data, encoding: String.Encoding.utf8) {
if let newValue = Double(str.trimEverything) {
self.progressIndicator.doubleValue = newValue
}
}
outHandle.waitForDataInBackgroundAndNotify()
} else {
// That means we've reached the end of the input.
NotificationCenter.default.removeObserver(progressObserver)
}
}
var terminationObserver : NSObjectProtocol!
terminationObserver = NotificationCenter.default.addObserver(
forName: Process.didTerminateNotification,
object: task, queue: nil)
{
notification -> Void in
// Process was terminated. Hence, progress should be 100%
self.progressIndicator.doubleValue = 100
NotificationCenter.default.removeObserver(terminationObserver)
}
task.launch()
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
// This is just a convenience extension so that I can trim
// off the extra newlines and white spaces before converting
// the input to a Double.
fileprivate extension String {
var trimEverything: String {
return self.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
}
}
,進度條發展到60%,然後一旦兒童進程完成,就跳到100%。
請務必讓子進程將其標準輸出設置爲行緩衝或在每行之後顯式刷新它。否則,它可能會將其輸出保存在內部緩衝區中,而不是實際寫入管道。 –
是的,第二個選項做到了。 – AthanasiusOfAlex