Process
Process copied to clipboard
Cannot get live output
In some cases I want to parse the output from a long running Process and selectively output it to the terminal, but it seems that is not currently supported? Any suggestions on where to look if I wanted to add it?
Hello @ccjensen thanks for the interest,
I think this will be in the posix spawn call https://github.com/oarrabi/Process/blob/291b63cf8233042537e5b8933c4d9d59c2b99375/Sources/Process/Run.swift#L36
I have researched this before and I remember that interactive was not possible but not 100% sure.
Would be awesome to do more research on it :)
I believe I had this working when I was working on the VaporToolbox and porting it to use posix spawn from system in the swift 2->3 transition. I believe this has been moved to https://github.com/vapor/console/blob/88e7b2347636c534ef3e8e537ffd31e551e9a161/Sources/Console/Terminal/Terminal.swift
I’ll have a look if I can dig this up again.
@feinstruktur That would be super helpful, I banged my head around this but couldnt figure it out :S
I’ve had a look through vapor console and extracted the function that runs the command and prints to the console just like system
used to. I haven’t looked into how this would be be integrated but it’s something that can be dropped in if you need the functionality:
// adopted from (with minor edits): https://github.com/vapor/console/blob/88e7b2347636c534ef3e8e537ffd31e551e9a161/Sources/Console/Terminal/Terminal.swift
import Foundation
public enum ExecuteError: Swift.Error {
case spawnProcess
case execute(code: Int)
case fileOrDirectoryNotFound
}
private var _pids: [UnsafeMutablePointer<pid_t>] = []
public func execute(program: String, arguments: [String]) throws {
let stdin = FileHandle.standardInput
let stdout = FileHandle.standardOutput
let stderr = FileHandle.standardError
try execute(
program: program,
arguments: arguments,
input: stdin.fileDescriptor,
output: stdout.fileDescriptor,
error: stderr.fileDescriptor
)
}
public func execute(program: String, arguments: [String], input: Int32? = nil, output: Int32? = nil, error: Int32? = nil) throws {
var pid = UnsafeMutablePointer<pid_t>.allocate(capacity: 1)
pid.initialize(to: pid_t())
defer {
pid.deinitialize()
pid.deallocate(capacity: 1)
}
let args = [program] + arguments
let argv: [UnsafeMutablePointer<CChar>?] = args.map{ $0.withCString(strdup) }
defer { for case let arg? in argv { free(arg) } }
var environment: [String: String] = ProcessInfo.processInfo.environment
let env: [UnsafeMutablePointer<CChar>?] = environment.map{ "\($0.0)=\($0.1)".withCString(strdup) }
defer { for case let arg? in env { free(arg) } }
#if os(macOS)
var fileActions: posix_spawn_file_actions_t? = nil
#else
var fileActions = posix_spawn_file_actions_t()
#endif
posix_spawn_file_actions_init(&fileActions);
defer {
posix_spawn_file_actions_destroy(&fileActions)
}
if let input = input {
posix_spawn_file_actions_adddup2(&fileActions, input, 0)
}
if let output = output {
posix_spawn_file_actions_adddup2(&fileActions, output, 1)
}
if let error = error {
posix_spawn_file_actions_adddup2(&fileActions, error, 2)
}
_pids.append(pid)
let spawned = posix_spawnp(pid, argv[0], &fileActions, nil, argv + [nil], env + [nil])
if spawned != 0 {
throw ExecuteError.spawnProcess
}
var result: Int32 = 0
_ = waitpid(pid.pointee, &result, 0)
result = result / 256
waitpid(pid.pointee, nil, 0)
if result == ENOENT {
throw ExecuteError.fileOrDirectoryNotFound
} else if result != 0 {
throw ExecuteError.execute(code: Int(result))
}
}
You can drop this into a file in this repo.
@feinstruktur This looks cool! Thanks for sharing. This works interactively since it does not capture the stdout and stderr. Is that correct?
Yes, it essentially routes everything through to the terminal and I'm using it in cases where I want to show intermediate output. If you plug that in and run a longer running command with intermediate output, like for instance find / -name "*.sh"
, through it you'll see what I mean.
I haven't tried but I'm sure you can grab stdout and stdout for reading in order to still return them as a result.
I would offer to try and integrate that but I'm not sure if/when I'd find the time!
@feinstruktur I have added this on my todo list. I think that if we provide our own dup of the stdin, then the interactiveness will be lost. I will try this flavour of posix spawn and report to this issue what I found.
Thank you for the hint :)
@nsomar Any updates?
@mominul I didnt really have a chance to look into this. I am afraid I wont have enough time to look at this anytime soon :(