How can I tell when a FileHandle has nothing left to be read?

此生再无相见时 提交于 2020-01-04 04:13:06

问题


I'm trying to use a Pipe's fileHandleForReading's readabilityHandler to read both the standardOutput and standardError of a Process. However, the moment the terminationHandler is called is actually before the moment my readabilityHandler is called for the first time.

I'm not sure why the process does this, but it means I'm not getting all the data, because I assume process termination means all output has been flushed to the pipe. Since this isn't the case, is there a way for me to tell when there is no more output to be read? I assume that involves checking if a FileHandle is still open, but I don't see an API for that.

Here's an example of the basic idea of what my code looks like:

let stdOutPipe = Pipe()
let stdErrPipe = Pipe()

stdOutPipe.fileHandleForReading.readabilityHandler = { stdOutFileHandle in
    let stdOutPartialData = stdOutFileHandle.readDataToEndOfFile()

    guard !stdOutPartialData.isEmpty else {
        print("Time to read, but nothing to be read?") // happens a lot
        return
    }

    self.tempStdOutStorage.append(stdOutPartialData)
}

stdErrPipe.fileHandleForReading.readabilityHandler = { stdErrFileHandle in
    let stdErrPartialData = stdErrFileHandle.readDataToEndOfFile()

    guard !stdErrPartialData.isEmpty else {
        print("Time to read, but nothing to be read?") // happens a lot
        return
    }

    self.tempStdErrStorage.append(stdErrPartialData)
}

process.standardOutput = stdOutPipe
process.standardError = stdErrPipe


process.terminationHandler = { process in
    notifyOfCompleteRead(stdOut: self.tempStdOutStorage, stdErr: self.tempStdErrStorage)
}

mySpecializedDispatchQueue.async(execute: process.launch)

回答1:


In the readabilityHandler you should use availableData to get the currently available data without blocking. Empty available data indicates EOF on the file handle, in that case the readability handler should be removed.

A dispatch group can be used to wait for EOF on both standard output and standard error after the process has finished.

Example:

let group = DispatchGroup()

group.enter()
stdOutPipe.fileHandleForReading.readabilityHandler = { stdOutFileHandle in
    let stdOutPartialData = stdOutFileHandle.availableData
    if stdOutPartialData.isEmpty  {
        print("EOF on stdin")
        stdOutPipe.fileHandleForReading.readabilityHandler = nil
        group.leave()
    } else {
        tempStdOutStorage.append(stdOutPartialData)
    }
}

group.enter()
stdErrPipe.fileHandleForReading.readabilityHandler = { stdErrFileHandle in
    let stdErrPartialData = stdErrFileHandle.availableData

    if stdErrPartialData.isEmpty  {
        print("EOF on stderr")
        stdErrPipe.fileHandleForReading.readabilityHandler = nil
        group.leave()
    } else {
        tempStdErrStorage.append(stdErrPartialData)
    }
}

process.standardOutput = stdOutPipe
process.standardError = stdErrPipe

process.launch()

process.terminationHandler = { process in
    group.wait()
    print("OUTPUT:", String(data: tempStdOutStorage, encoding: .utf8)!)
    print("ERROR: ", String(data: tempStdErrStorage, encoding: .utf8)!)
}


来源:https://stackoverflow.com/questions/52335435/how-can-i-tell-when-a-filehandle-has-nothing-left-to-be-read

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!