0

This question inspired me to another related question. Imagine that you start

% nohup a.sh &

and then want b.sh to execute when a.sh is finished (E.g., b.sh will process the output from a.sh. Both a.sh and b.sh are long running processes that takes hours to finish.)

Basically you want to simulate

% nohup a.sh && b.sh

but you forgot to add b.sh when you launched a.sh. Can this be achieved? Preferably in zsh.

d-b
  • 1,891
  • 3
  • 18
  • 30
  • Note that A && B is not run B after A is finished, it's run B after A is finished and only if A was successful (exited with a 0 exit status). To run B after A, it's just A; B or A and B on separate lines. – Stéphane Chazelas Nov 11 '23 at 07:21

2 Answers2

3

If the shell that started the process still knows about it (try jobs), then the shell built in wait can wait for it to finish. Something like this might work:

$ jobs
[1] nohup a.sh
$ wait -f %1 && nohup b.sh &

If you have lost the association in the shell then the pidwait command might work in a similar way, although it probably can't capture the exit value of the first process, and may run the second one even fi the first fails, unlike what && does.

user10489
  • 6,740
  • zsh's wait has no -f option. There's a -f in the wait builtin of bash, but that code wouldn't work in bash as wait there would be run in a subshell in background so wouldn't be able to wait for a.sh which wouldn't be its child. – Stéphane Chazelas Nov 11 '23 at 07:26
  • Excellent. I don't have pidwait, does MacOS have something similar? Or is it a shell function? – d-b Nov 11 '23 at 11:23
3

Only the parent process (or the child subreaper if any or init if the parent died), can retrieve the exit status of a process.

From the shell where you started the command, you can wait for it, and start b.sh in background if a.sh was successful with:

wait $pid && nohup b.sh &

In zsh or:

wait -f "$pid" && { nohup b.sh & }

In recent versions of bash (the -f for wait not to return when the process is only stopped; that's the default behaviour in zsh).

However the wait has to run in foreground. If run in background it would be in a child shell process and therefore would no longer be the parent of the process running nohup a.sh.

To retrieve the exit status of a process that is not your child one approach is to use a debugger and intercept the _exit(), exit_group() or equivalent system calls.

For instance, on Linux, one could do:

ret=$(strace -qqqa0 -e signal=none -e exit_group -p "$pid" 2>&1 | grep -Po '\(\K\d+')

The macos equivalent would likely involve a truss or dtrace command, but I don't have a machine to test that on.