我试图通过使用Foundation库中的Process来运行shell(bash
/ zsh
/ etc
)。
我知道如何使用Pipe作为其他命令行可执行文件的标准输入/输出/错误,但似乎shell程序要求标准输入/输出/错误文件是终端设备文件。
看起来这些是为每个新的shell实例创建的名为/dev/ttys<3 digit number>
的文件。我如何自己创建这些文件并在swift中使用它们?
解:
所以事实证明这比我想象的要容易得多。以下是创建一对主和从属伪终端FileHandle
对象所需的步骤:
Darwin
模块。posix_openpt(int oflag)
获取可用的主伪终端设备的文件描述符。grantpt(int filedes)
建立相应的从属伪终端设备的所有权。unlockpt(int filedes)
来解锁从属伪终端设备。ptsname(int filedes)
获取slave伪终端设备的路径。FileHandle
对象。在步骤3-5中,filedes
是从步骤1调用posix_openpt(int oflag)
返回的文件描述符。对于主伪终端设备文件的读写权限,请在步骤1中使用O_RDRW
作为oflag
参数。
我们保持bash
会话存活并让它运行tty
命令的示例代码:
import Foundation
import Darwin
class A: NSObject {
var task: Process?
var slaveFile: FileHandle?
var masterFile: FileHandle?
override init() {
self.task = Process()
var masterFD: Int32 = 0
masterFD = posix_openpt(O_RDWR)
grantpt(masterFD)
unlockpt(masterFD)
self.masterFile = FileHandle.init(fileDescriptor: masterFD)
let slavePath = String.init(cString: ptsname(masterFD))
self.slaveFile = FileHandle.init(forUpdatingAtPath: slavePath)
self.task!.executableURL = URL(fileURLWithPath: "/bin/bash")
self.task!.arguments = ["-i"]
self.task!.standardOutput = slaveFile
self.task!.standardInput = slaveFile
self.task!.standardError = slaveFile
}
func run() {
DispatchQueue.global(qos: .userInitiated).async { [weak self] in
guard let self = self else {
return
}
do {
try self.task!.run()
} catch {
print("Something went wrong.\n")
}
}
let data = self.masterFile!.availableData
let strData = String(data: data, encoding: String.Encoding.utf8)!
print("Output: "+strData)
self.masterFile!.write("tty\u{0D}".data(using: String.Encoding.utf8)!)
sleep(1)
let data1 = self.masterFile!.availableData
let strData1 = String(data: data1, encoding: String.Encoding.utf8)!
print("Output: "+strData1)
}
}
let a = A()
a.run()