SwiftSlash
SwiftSlash copied to clipboard
Recommended way to print stdout and stderr to the console in real time
I'd like to allow commands that I call with SwiftSlash to print stdout and stderr to the console like if they were being run directly in a shell. Is there a way built into SwiftSlash for me to do this? Or do you have a recommendation on how best to accomplish this?
My current method of accomplishing this is to create a task group which with two tasks run in parallel:
let processInterface = ProcessInterface(command: command)
try await processInterface.launch()
await withTaskGroup(of: Void.self) { group in
group.addTask(priority: .high) {
for await chunk in await processInterface.stdout {
guard let string = String(data: chunk, encoding: .utf8) else { continue }
print(string)
}
}
group.addTask(priority: .high) {
for await chunk in await processInterface.stderr {
guard let string = String(data: chunk, encoding: .utf8) else { continue }
print(string)
}
}
}
This does work, but the output is not streamed in real time. It gets printed to the console in short bursts. It also sometimes gets printed out of order.
Here's a gif showing what I mean, using a download with curl
, first run directly, then run via a Swift Package using SwiftShell:
This is the Swift package that was being run: SwiftSlashExample.zip
Any tips or ideas?
I'd love if there were an option on ProcessInterface
or something where I could just say "also print stdout and/or stderr to the console".
I like this idea. Let me see what I can come up with.
@hisaac as far as tips for current release, I don’t have any that can improve your situation yet. The batching/timing issue is a deeply rooted symptom that I will address in the next major cycle. My absolute deadline for v4 is when Swift 6 is released, however, I’m currently feeling good about the development trajectory and feel it could easily be done this year.
Stay tuned.
Sounds good, I look forward to seeing what you come up with for version 4!
I ran across something that might be of use here: An environment variable called NSUnbufferedIO
.
I first noticed it in Realm's build script here: https://github.com/realm/realm-swift/blob/6b992e508b1fab07d47775f2fd7485fd5f110893/build.sh#L121
And after a quick search, I found some information about it in an NSHipster article:
Although unlikely, you may come across a situation where you want logging to
stdout
to be unbuffered (ensuring that the output has been written before continuing). You can set that with theNSUnbufferedIO
environment variable. If set toYES
, Foundation will use unbuffered I/O forstdout
(stderr
is unbuffered by default).
I don't know if that's helpful here, but it made me think of this conversation, and that it might be a useful nugget.
I hope SwiftSlash v4 is coming along nicely! 👋