kareman / SwiftShell

A Swift framework for shell scripting.
https://kareman.github.io/SwiftShell
MIT License
1.03k stars 87 forks source link

[Question] how can i get async run to give me result like sync run? #79

Closed patchthecode closed 4 years ago

patchthecode commented 5 years ago

OK. Maybe I do not understand async runs but..

If i run a command like this

let result = run(bash: "some command")

I get a RunOutput in which i can check the

let error: CommandError?
let stdout: String
let stderror: String
let exitcode: Int
let succeeded: Bool

With asyncRuns, in the onCompletion closure, i get an AsyncCommand instead on RunOutput.

the stderror is not a ReadableStream and not a String Basically, what i am asking is,

let myRun = SwiftShell.runAsync(bash: "sd").onCompletion { command in
    // is there a way i can get the same results as RunOut
    // inside here?
}
patchthecode commented 5 years ago

nevermind...

command.stderror.read()
patchthecode commented 5 years ago

just a followup though, can runasync return a RunOutput ?

in particular, is there a let error: CommandError? with runasync?

kareman commented 5 years ago

To get the error from an asynchronous command, you can do this:

do {
    let myRun = SwiftShell.runAsync(bash: "sd")
    try myRun.finish() 
} catch {
    ...
}

Maybe the finish command should return RunOutput instead, that might make more sense. I've been wanting to clean up the SwiftShell API for a while, as it is very confusing. But there are so many variables to consider, like is the command asynchronous? Should standard error and standard output be combined, printed, cached, or a combination? Should it throw an error if the exit code is not 0? Is the command run directly or using bash or another commandline interpreter?

patchthecode commented 5 years ago

Yea, I think that the async version of the code should return the same thing as the sync for consistency.

maybe something like

SwiftShell.runAsync(bash: "sd") { runoutput in
....
}

Should standard error and standard output be combined, printed, cached, or a combination?

Why not have all 3?

RunOutput {
   let stdout: String
   let stderror: String
   let combined: String // stdout+stderror
}

Also i noticed that on runAsync

SwiftShell.runAsync(bash: consoleCommand)
            .onCompletion { command in
                command.stdout.read()   // can be called `command.stdout` for consistency
                command.stderror.read() // can be called `command.error` for consistency
                command.exitcode() // can be called `command.exitcode` for consistency
            }

Also, on the async run, calling .read causes the data to be flushed. I can only call it once. Maybe you should cache it instead like you suggested?

Should it throw an error if the exit code is not 0?

No, it should complete like you have done. Leave it up to the developer to determine is the "error" is an error he wishes to act on.

Is the command run directly or using bash or another commandline interpreter?

I like bash it has better syntax than separating ever word with "a", "quote", "for", "every", "command". Maybe you can give the developer to choice to choose what commandline interpreter he wishes to use, because you'll have no idea what they have on their system. Take for example this new one for catalina.

Not sure if what I said makes any sense because you know the library better than i do. But if you need any more ideas, then i can suggest.

kareman commented 4 years ago

Thank you for the suggestions, I’ll consider them if I am able to develop a new API. I’ll close this issue, as the original problem has been resolved.