You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Hi, I was playing around with go-task and I couldn't get this to work: sleep 10 & echo $!. It would only print a new line but not the PID of the sleep call. @andreynering suggested I post an issue here.
This library is written in Go, so it can't spawn sub-processes. If I make $! work, it will always print the same as $$. That's part of the reason why I've been hesitant to add it.
I guess I could add it, but that behavior might confuse lots of people. I should add docs about these gotchas in the interpreter.
The only portable and stable way to have multiple processes would be for the interpreter to run from a shell, say gosh, and then spawning subshells via exec.Command("gosh", ...). But the point of the interpreter is being embeddable into a binary, so you'd lose that. You'd also lose other features like controlling the interpreter via modules and the context.
There's also the question of why you'd need $$ and $! in the first place. If you want to write portable shell scripts, how can you safely use them for anything other than printing them?
All in all, I decided not to do it by default because it's too much hassle for little gain. However, I'm open to doing the option above - depending on its own shell executable - via an option like #171.
There's always the option of using virtual PIDs, to make it so that $$ and $! could be different among goroutines. But I presume that will open a can of worms.
There's also the question of why you'd need $$ and $! in the first place. If you want to write portable shell scripts, how can you safely use them for anything other than printing them?
I'm not trying to reopen the issue, thought I can give you an answer regarding my current problem.
I would like to parallel build multiple things, and if one of them fail, exit !=0.
The only way I could think of as of now is to spawn subshells then wait, unfortunately wait is pretty basic, and if the return code of the subshell is needed (and it is in this case), wait has to be called with a the pid of the process which you want to return the pid from.
Therefore, without any indicator to point wait to, no return code can be gathered from a subshell.
Thanks @Ckarles, that's a valid point. I'll reopen the issue for now.
One way we could possibly do this is by having "virtual" PIDs by default, where each PID just maps to one of the goroutines running an interpreter's subshell. Then, we could still have wait work for those.
We'd have to try to avoid conflicts between those per-goroutine PIDs and the real host system's PIDs, though. Maybe by making them start at a really big number, like 64k?
Note that I went for a "g" prefix to differentiate these virtual PIDs, representing a goroutine in memory, from the real PIDs representing processes on the host system.
Reacted by JonZeolla, James Sansbury and Jurgen Braam
Can we revisit this? I've run into this and am using your project as a shell in a custom linux os called GoNix and this behaviour is currently a bit of a bug for me.
Activity
mvdan commentedon Mar 13, 2018
This library is written in Go, so it can't spawn sub-processes. If I make
$!work, it will always print the same as$$. That's part of the reason why I've been hesitant to add it.I guess I could add it, but that behavior might confuse lots of people. I should add docs about these gotchas in the interpreter.
rhd commentedon Mar 14, 2018
Yea, printing
$$is mostly useless. No way to useos/execto spawn the process?mvdan commentedon Mar 16, 2018
How, exactly?
os/execexecutes programs. It does not allow forking alone, because it wouldn't work in Go. For example, see https://stackoverflow.com/questions/28370646/how-do-i-fork-a-go-processThe only portable and stable way to have multiple processes would be for the interpreter to run from a shell, say
gosh, and then spawning subshells viaexec.Command("gosh", ...). But the point of the interpreter is being embeddable into a binary, so you'd lose that. You'd also lose other features like controlling the interpreter via modules and the context.There's also the question of why you'd need $$ and $! in the first place. If you want to write portable shell scripts, how can you safely use them for anything other than printing them?
All in all, I decided not to do it by default because it's too much hassle for little gain. However, I'm open to doing the option above - depending on its own shell executable - via an option like #171.
There's always the option of using virtual PIDs, to make it so that $$ and $! could be different among goroutines. But I presume that will open a can of worms.
mvdan commentedon Mar 23, 2018
Ping, @rhd? Did you have an implementation idea in mind?
mvdan commentedon Apr 22, 2018
Timed out after a month - still open to suggestions, but I see no clear way forward right now.
[-]How to get PID?[/-][+]interp: how to get PID?[/+]Ckarles commentedon May 29, 2021
I'm not trying to reopen the issue, thought I can give you an answer regarding my current problem.
I would like to parallel build multiple things, and if one of them fail,
exit !=0.The only way I could think of as of now is to spawn subshells then
wait, unfortunatelywaitis pretty basic, and if the return code of the subshell is needed (and it is in this case),waithas to be called with a the pid of the process which you want to return the pid from.Therefore, without any indicator to point wait to, no return code can be gathered from a subshell.
mvdan commentedon May 29, 2021
Thanks @Ckarles, that's a valid point. I'll reopen the issue for now.
One way we could possibly do this is by having "virtual" PIDs by default, where each PID just maps to one of the goroutines running an interpreter's subshell. Then, we could still have
waitwork for those.We'd have to try to avoid conflicts between those per-goroutine PIDs and the real host system's PIDs, though. Maybe by making them start at a really big number, like 64k?
[-]interp: how to get PID?[/-][+]interp: consider some support for PIDs[/+]jubr commentedon Mar 5, 2025
Running into this again today, any new insights?
mvdan commentedon Mar 29, 2025
This is implemented as of master:
Note that I went for a "g" prefix to differentiate these virtual PIDs, representing a goroutine in memory, from the real PIDs representing processes on the host system.
interp: initial support for $! and wait PID
33 remaining items
merge: Rust handles and callback bridge with isolated context and status
interp: reserve persistent stream workers in pipeline process groups
interp: verify stream job terminal ownership and Ctrl-C lifecycle
interp+polyglot: stream Python and Rust iterators on persistent workers
lower: align foreign-error assertions with accepted worker and callba…
lower: keep standalone runtime declarations independent of worker pac…
lower: make native parity and runtime-plan fixtures executable on Win…
polyglot+interp: preserve Python and toolchain behavior on Windows
fix: isolate live process output locks from foreground consumers
interp: review authenticated Windows Go 1.27.1 toolchains
interp: scope Windows toolchain review to runtime execution
polyglot: preserve Windows callback protocol during output capture
fix: synchronize foreign iterator output with shell diagnostics
polyglot: report native worker exits on Windows
prologic commentedon Oct 18, 2026
Can we revisit this? I've run into this and am using your project as a shell in a custom linux os called GoNix and this behaviour is currently a bit of a bug for me.