That's one of the known deviations¹ of zsh compared to sh (or csh for that matters).
In sh:
A && B &
is short for:
(A && B) &
That is, it runs a subshell that runs that and-or list in background / asynchronously.
In zsh,
A && B &
Runs A
synchronously, waits for it, and if successful, runs B
in background / asynchronously.
That's noted in its manual (see info zsh list
):
A list is a sequence of zero or more sublists, in which each sublist
is terminated by ;
, &
, &|
, &!
, or a newline. This terminator
may optionally be omitted from the last sublist in the list when the
list appears as a complex command inside (...)
or {...}
. When a
sublist is terminated by ;
or newline, the shell waits for it to
finish before executing the next sublist. If a sublist is terminated by
a &
, &|
, or &!
, the shell executes the last pipeline in it in the
background, and does not wait for it to finish (note the difference from
other shells which execute the whole sublist in the background). A
backgrounded pipeline returns a status of zero.
(emphasis mine).
And in the FAQ:
In cmd1 && cmd2 &
, only cmd2
instead of the whole expression is run in the background in zsh. The manual implies this is a bug. Use { cmd1 && cmd2 } &
as a workaround.
To run a subshell that does A && B
in background like in other shells, use:
(A && B) &
Or as suggested by that FAQ entry:
{A && B} &
Which are equivalent since we need to fork a process anyway if it has to run asynchronously (and the main shell process to carry on with the rest of the script at the same time).
So here:
(nohup a.sh && nohup b.sh) &
Though if you just want the process running that subshell and the other processes it spawns to be immune to the SIGHUP signal:
(trap '' HUP; A && B) &
(you can also add some < /dev/null >> nohup.out 2>&1
to even more closely mimic nohup)
Conversely, if you want the same behaviour as zsh's in other sh-like shells:
A && { B & }
(bearing in mind that in shells other than zsh, when job control is no enabled, the stdin of lists run asynchronously is redirected to /dev/null)
¹ It's possible that deviation may have been unintentional as the wording of the documentation before that 2000 change to fix it to align with the actual behaviour (and that goes back all the way to zsh 1.0 in 1990) described the sh behaviour. That's also the conclusion of the FAQ that has had that "The manual implies this is a bug" since 1997. But it would be too late to change it now (but one would argue it should be changed in sh, csh or ksh emulations).
b.sh
need to wait fora.sh
to finish? – terdon Nov 10 '23 at 13:01nohup
is pointless in any job control shell (including zsh, bash, ksh, csh). – chicks Nov 14 '23 at 22:12