用swift在macOS中打开新的伪终端设备文件

问题描述 投票:2回答:1

我试图通过使用Foundation库中的Process来运行shell(bash / zsh / etc)。

我知道如何使用Pipe作为其他命令行可执行文件的标准输入/输出/错误,但似乎shell程序要求标准输入/输出/错误文件是终端设备文件。

看起来这些是为每个新的shell实例创建的名为/dev/ttys<3 digit number>的文件。我如何自己创建这些文件并在swift中使用它们?

swift macos
1个回答
2
投票

解:

所以事实证明这比我想象的要容易得多。以下是创建一对主和从属伪终端FileHandle对象所需的步骤:

  1. 导入Darwin模块。
  2. 调用posix_openpt(int oflag)获取可用的主伪终端设备的文件描述符。
  3. 调用grantpt(int filedes)建立相应的从属伪终端设备的所有权。
  4. 调用unlockpt(int filedes)来解锁从属伪终端设备。
  5. 调用ptsname(int filedes)获取slave伪终端设备的路径。
  6. 使用步骤2中的文件描述符和步骤5中的路径创建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()
© www.soinside.com 2019 - 2024. All rights reserved.