如何从终端

时间:2017-03-03 02:02:35

标签: swift nsfilehandle

我有一个从FileHandle.standardInput读取的Swift程序(在Objective-C中,这将是+[NSFileHandle fileHandleWithStandardInput]。当它到达输入流上的文件结尾时它应该终止读取,但是当我使用Terminal(在macOS Sierra上)作为输入运行它,当我按Ctrl + D时它不会检测到文件结尾。

以下是我正在做的简化示例。该程序只是从标准输入读取并将其读取的内容写入标准输出:

#!/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)
}

我希望readData(ofLength:)在到达文件末尾时返回Datacount为零的对象。

当我使用重定向到标准输入的文件运行时,如下所示:

./echo.swift < foo.txt

它写出文件并终止。

但是,如果我像这样运行它:

./echo.swift

然后键入一些文本并按Ctrl + D,我希望它将Ctrl + D视为文件结束并终止。但它没有那样做。它只是保持运行和回声线。如果我一遍又一遍地按Ctrl + D,它最终会终止,但那不是我想要的。

更改bufferSize似乎没有帮助。如果我将其设置为1,我会得到相同的行为。

我怀疑我需要在stdin文件描述符或终端设备上设置某种缓冲参数,或者捕获信号或其他东西,但我不知道是什么。

我知道我可以使用C stdio fread() API来正确检测来自终端的文件结束条件,或者我可以使用Swift的readLine(_:)来读取标准输入担心文件句柄/描述符,但我想知道是否有办法用FileHandle或原始文件描述符执行此操作而不重新实现C stdio。

更新:花了一个小时回顾Apple的LibC source后,我总结说“这很复杂”,所以现在我只是在我的程序中使用fread(..., stdin)。但是我仍然想知道是否有一些简单的方法可以让它与FileHandle一起使用。

1 个答案:

答案 0 :(得分:1)

今天,Swift 5,像这样...

while (FileHandle.standardInput.availableData.count > 0) {
  FileHandle.standardOutput.write(FileHandle.standardInput.availableData)
}

简单并且确实像cat程序一样工作,为此需要进行一些调整。

行为正确的更好:

var data: Data
repeat {
  data = FileHandle.standardInput.availableData
  FileHandle.standardOutput.write(data)
} while (data.count > 0)

有关它的文档:

  

当前可通过接收器获得的数据,最大为NSData对象可以表示的最大大小。

     

如果接收者是文件,则此方法返回通过从当前文件指针读取文件到文件末尾而获得的数据。如果接收方是通信通道,则此方法读取数据缓冲区并返回数据;如果没有可用数据,则该方法将阻塞。如果到达文件末尾,则返回一个空的数据对象。如果尝试确定文件句柄类型失败或尝试从文件或通道读取失败,则此方法引发NSFileHandleOperationException