SwiftSlash icon indicating copy to clipboard operation
SwiftSlash copied to clipboard

Recommended way to print stdout and stderr to the console in real time

Open hisaac opened this issue 1 year ago • 6 comments

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?

hisaac avatar Aug 29 '23 19:08 hisaac

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:

CleanShot 2023-08-29 at 15 12 42

This is the Swift package that was being run: SwiftSlashExample.zip

Any tips or ideas?

hisaac avatar Aug 29 '23 20:08 hisaac

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".

hisaac avatar Aug 29 '23 20:08 hisaac

I like this idea. Let me see what I can come up with.

tannerdsilva avatar Sep 05 '23 15:09 tannerdsilva

@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.

tannerdsilva avatar Sep 05 '23 18:09 tannerdsilva

Sounds good, I look forward to seeing what you come up with for version 4!

hisaac avatar Sep 05 '23 19:09 hisaac

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 the NSUnbufferedIO environment variable. If set to YES, Foundation will use unbuffered I/O for stdout (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! 👋

hisaac avatar Nov 02 '23 20:11 hisaac