Launch a terminal editor such as vi or nano in Swift

Viewed 89

I' trying to launch a terminal editor such as vi or nano in a command line program written in Swift.

Here's a naive attempt, I'm guessing that the standard input and output should be different. The program outputs nothing.

let task = Process()
task.launchPath = "/usr/bin/env"
task.arguments = ["nano"]
task.standardInput = FileHandle(forReadingAtPath: "/dev/tty")
task.standardOutput = FileHandle(forWritingAtPath: "/dev/tty")
task.launch()
task.waitUntilExit()
2 Answers

This example will launch the 'vi' editor into a Terminal window. May be run in an Xcode swift project by copy/pasting the code into a newly added 'main.swift' file and deleting the pre-existing AppDelegate. Will also need to turn off the Sandbox.

import Cocoa

let process = Process()
let pipe = Pipe()

process.executableURL = URL(fileURLWithPath:"/bin/sh/")
var args : [String]!
args = []
args.append("-c")
args.append("open '/usr/bin/vi'")
process.arguments = args
process.standardOutput = pipe
process.standardError = pipe
process.terminationHandler = { (process) in
  print("\ndidFinish: \(!process.isRunning)")
}
let fileHandle = pipe.fileHandleForReading
do {
  try process.run()
  let outStr = String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8)
  print(outStr as Any)
} catch { print(String(data: fileHandle.readDataToEndOfFile(), encoding: .utf8) as Any)}

// **** main.swift **** //
let app = NSApplication.shared
app.setActivationPolicy(.regular)
app.activate(ignoringOtherApps:true)
app.run()

It seems to work with posix_spawn:

#if os(Linux)
import Glibc
#else
import Darwin
#endif

public struct PosixProcess {
    public let executablePath: String
    public let arguments: [String]

    public init(_ executablePath: String, _ arguments: String...) {
        self.executablePath = executablePath
        self.arguments = arguments
    }

    public func spawn() throws {
        var pid: pid_t = 0
        let path = executablePath.withCString(strdup)!
        let args = arguments.map { $0.withCString(strdup)! }
        defer {
            ([path] + args).forEach { free($0) }
        }
        if posix_spawn(&pid, path, nil, nil, [path] + args + [nil], environ) < 0 {
            // throw some error with `errno`
        }
        if waitpid(pid, nil, 0) < 0 {
            // throw some error with `errno`
        }
    }
}

try PosixProcess("/usr/bin/env", "nano").spawn()
Related