I have a Swift program that reads from FileHandle.standardInput
(in Objective-C, this would be +[NSFileHandle fileHandleWithStandardInput]
. It should terminate reading when it hits end-of-file on the input stream, but when I run it using the Terminal (on macOS Sierra) as input, it doesn't detect end-of-file when I hit Ctrl+D.
Here is a simplified example of what I'm doing. This program simply reads from standard input and writes what it has read to standard output:
#!/usr/bin/swift
import Foundation
let input = FileHandle.standardInput
let output = FileHandle.standardOutput
let bufferSize = 1024
var data = input.readData(ofLength: bufferSize)
while data.count > 0 {
output.write(data)
data = output.readData(ofLength: bufferSize)
}
I expect readData(ofLength:)
to return a Data
object with a count
of zero when it reaches end of file.
When I run with a file redirected to standard input, like this:
./echo.swift < foo.txt
It writes out the file and terminates.
However, if I run it like this:
./echo.swift
and then type some text and hit Ctrl+D, I expect it to treat the Ctrl+D as end-of-file and terminate. But it doesn't do that. It just keeps running and echoing lines. It will eventually terminate if I hit Ctrl+D over and over, but that's not what I want.
Changing the bufferSize
doesn't seem to help. I get the same behavior if I set it to 1
.
I suspect I need to set some sort of buffering parameter on the stdin
file descriptor, or the terminal device, or catch a signal, or something, but I don't know what.
I know that I could use the C stdio fread()
API instead, which properly detects an end-of-file condition from the terminal, or I could use Swift's readLine(_:)
to read from standard input without worrying about file handles/descriptors, but I want to know if there is a way to do this with FileHandle
or the raw file descriptor without re-implementing C stdio.
Update: After spending an hour reviewing Apple's LibC source, I concluded "It's complicated" and so now I'm just using fread(..., stdin)
in my program. But I'd still like to know if there is some simple way to get this to work with FileHandle
.
readLine
function. – Armoury